This is an automated email from the ASF dual-hosted git repository. adelapena pushed a commit to branch cassandra-3.0 in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/cassandra-3.0 by this push: new ffc4c89c3d Fix issue where frozen maps may not be serialized in the correct order ffc4c89c3d is described below commit ffc4c89c3df7ad0ae73ebefdcb7e15a2790c0a52 Author: Doug Rohrer <droh...@apple.com> AuthorDate: Tue May 17 15:09:16 2022 -0400 Fix issue where frozen maps may not be serialized in the correct order patch by Doug Rohrer, Francisco Guerrero and Yifan Cai; reviewed by Andrés de la Peña and Caleb Rackliffe for CASSANDRA-17623 Co-authored-by: Doug Rohrer <droh...@apple.com> Co-authored-by: Francisco Guerrero <frank.guerr...@gmail.com> Co-authored-by: Yifan Cai <y...@apache.org> --- CHANGES.txt | 1 + src/java/org/apache/cassandra/cql3/Maps.java | 11 +- src/java/org/apache/cassandra/cql3/Sets.java | 2 +- test/unit/org/apache/cassandra/cql3/CQLTester.java | 29 +++- .../apache/cassandra/cql3/ColumnConditionTest.java | 2 +- .../cql3/validation/entities/CollectionsTest.java | 74 ++++++++ .../cassandra/io/sstable/CQLSSTableWriterTest.java | 192 +++++++++++++++++++++ 7 files changed, 303 insertions(+), 8 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 767741bdd7..c49c33224f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0.28 + * Fix issue where frozen maps may not be serialized in the correct order (CASSANDRA-17623) * Suppress CVE-2022-24823 (CASSANDRA-17633) * fsync TOC and digest files (CASSANDRA-10709) diff --git a/src/java/org/apache/cassandra/cql3/Maps.java b/src/java/org/apache/cassandra/cql3/Maps.java index b21aca39e5..e56788ee6f 100644 --- a/src/java/org/apache/cassandra/cql3/Maps.java +++ b/src/java/org/apache/cassandra/cql3/Maps.java @@ -156,9 +156,9 @@ public abstract class Maps public static class Value extends Term.Terminal { - public final Map<ByteBuffer, ByteBuffer> map; + public final SortedMap<ByteBuffer, ByteBuffer> map; - public Value(Map<ByteBuffer, ByteBuffer> map) + public Value(SortedMap<ByteBuffer, ByteBuffer> map) { this.map = map; } @@ -170,7 +170,8 @@ public abstract class Maps // Collections have this small hack that validate cannot be called on a serialized object, // but compose does the validation (so we're fine). Map<?, ?> m = type.getSerializer().deserializeForNativeProtocol(value, version); - Map<ByteBuffer, ByteBuffer> map = new LinkedHashMap<>(m.size()); + // We depend on Maps to be properly sorted by their keys, so use a sorted map implementation here. + SortedMap<ByteBuffer, ByteBuffer> map = new TreeMap<>(type.getKeysType()); for (Map.Entry<?, ?> entry : m.entrySet()) map.put(type.getKeysType().decompose(entry.getKey()), type.getValuesType().decompose(entry.getValue())); return new Value(map); @@ -236,7 +237,7 @@ public abstract class Maps public Terminal bind(QueryOptions options) throws InvalidRequestException { - Map<ByteBuffer, ByteBuffer> buffers = new TreeMap<ByteBuffer, ByteBuffer>(comparator); + SortedMap<ByteBuffer, ByteBuffer> buffers = new TreeMap<>(comparator); for (Map.Entry<Term, Term> entry : elements.entrySet()) { // We don't support values > 64K because the serialization format encode the length as an unsigned short. @@ -451,7 +452,7 @@ public abstract class Maps if (value == null) return; - Map<ByteBuffer, ByteBuffer> elements = ((Value) value).map; + SortedMap<ByteBuffer, ByteBuffer> elements = ((Value) value).map; for (Map.Entry<ByteBuffer, ByteBuffer> entry : elements.entrySet()) params.addCell(column, CellPath.create(entry.getKey()), entry.getValue()); } diff --git a/src/java/org/apache/cassandra/cql3/Sets.java b/src/java/org/apache/cassandra/cql3/Sets.java index 4d8f61b693..5627f6b9c1 100644 --- a/src/java/org/apache/cassandra/cql3/Sets.java +++ b/src/java/org/apache/cassandra/cql3/Sets.java @@ -72,7 +72,7 @@ public abstract class Sets // We've parsed empty maps as a set literal to break the ambiguity so // handle that case now if (receiver.type instanceof MapType && elements.isEmpty()) - return new Maps.Value(Collections.<ByteBuffer, ByteBuffer>emptyMap()); + return new Maps.Value(Collections.emptySortedMap()); ColumnSpecification valueSpec = Sets.valueSpecOf(receiver); Set<Term> values = new HashSet<>(elements.size()); diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java b/test/unit/org/apache/cassandra/cql3/CQLTester.java index ddeb9dae5d..fe8ed5b9c3 100644 --- a/test/unit/org/apache/cassandra/cql3/CQLTester.java +++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java @@ -33,6 +33,8 @@ import java.util.stream.Collectors; import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + import org.junit.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -795,6 +797,10 @@ public abstract class CQLTester return Schema.instance.getCFMetaData(KEYSPACE, currentTable()); } + protected com.datastax.driver.core.ResultSet executeNet(String query, Object... values) throws Throwable + { + return sessionNet().execute(formatQuery(query), values); + } protected com.datastax.driver.core.ResultSet executeNet(int protocolVersion, String query, Object... values) throws Throwable { return sessionNet(protocolVersion).execute(formatQuery(query), values); @@ -941,6 +947,13 @@ public abstract class CQLTester assertRowsNet(PROTOCOL_VERSIONS.get(PROTOCOL_VERSIONS.size() - 1), result, rows); } + protected void assertRowCountNet(ResultSet r1, int expectedCount) + { + Assert.assertFalse("Received a null resultset when expected count was > 0", expectedCount > 0 && r1 == null); + int actualRowCount = Iterables.size(r1); + Assert.assertEquals(String.format("expected %d rows but received %d", expectedCount, actualRowCount), expectedCount, actualRowCount); + } + public static void assertRows(UntypedResultSet result, Object[]... rows) { if (result == null) @@ -1549,13 +1562,27 @@ public abstract class CQLTester return ImmutableSet.copyOf(values); } + // LinkedHashSets are iterable in insertion order, which is important for some tests + protected LinkedHashSet<Object> linkedHashSet(Object...values) + { + LinkedHashSet<Object> s = new LinkedHashSet<>(values.length); + s.addAll(Arrays.asList(values)); + return s; + } + protected Object map(Object...values) + { + return linkedHashMap(values); + } + + // LinkedHashMaps are iterable in insertion order, which is important for some tests + protected static LinkedHashMap<Object, Object> linkedHashMap(Object...values) { if (values.length % 2 != 0) throw new IllegalArgumentException(); int size = values.length / 2; - Map m = new LinkedHashMap(size); + LinkedHashMap<Object, Object> m = new LinkedHashMap<>(size); for (int i = 0; i < size; i++) m.put(values[2 * i], values[(2 * i) + 1]); return m; diff --git a/test/unit/org/apache/cassandra/cql3/ColumnConditionTest.java b/test/unit/org/apache/cassandra/cql3/ColumnConditionTest.java index 71524c5821..b39dca7c85 100644 --- a/test/unit/org/apache/cassandra/cql3/ColumnConditionTest.java +++ b/test/unit/org/apache/cassandra/cql3/ColumnConditionTest.java @@ -443,7 +443,7 @@ public class ColumnConditionTest { ColumnDefinition definition = ColumnDefinition.regularDef("ks", "cf", "c", ListType.getInstance(Int32Type.instance, true)); - Map<ByteBuffer, ByteBuffer> placeholderMap = new TreeMap<>(); + SortedMap<ByteBuffer, ByteBuffer> placeholderMap = new TreeMap<>(); placeholderMap.put(ONE, ONE); Maps.Value placeholder = new Maps.Value(placeholderMap); diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java index 918033eca3..fff476ba57 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java @@ -25,6 +25,7 @@ import java.util.UUID; import org.junit.Test; +import com.datastax.driver.core.utils.UUIDs; import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.utils.FBUtilities; @@ -1042,4 +1043,77 @@ public class CollectionsTest extends CQLTester assertInvalidMessage("Invalid map literal for m: value (1, '1', 1.0, 1) is not of type frozen<tuple<int, text, double>>", "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})"); } + + /* + Tests for CASSANDRA-17623 + Before CASSANDRA-17623, parameterized queries with maps as values would fail because frozen maps were + required to be sorted by the sort order of their key type, but weren't always sorted correctly. + Also adding tests for Sets, which did work because they always used SortedSet, to make sure this behavior is maintained. + We use `executeNet` in these tests because `execute` passes parameters through CqlTester#transformValues(), which calls + AbstractType#decompose() on the value, which "fixes" the map order, but wouldn't happen normally. + */ + + @Test + public void testInsertingMapDataWithParameterizedQueriesIsKeyOrderIndependent() throws Throwable + { + UUID uuid1 = UUIDs.timeBased(); + UUID uuid2 = UUIDs.timeBased(); + createTable("CREATE TABLE %s (k text, c frozen<map<timeuuid, text>>, PRIMARY KEY (k, c));"); + executeNet("INSERT INTO %s (k, c) VALUES ('0', ?)", linkedHashMap(uuid1, "0", uuid2, "1")); + executeNet("INSERT INTO %s (k, c) VALUES ('0', ?)", linkedHashMap(uuid2, "3", uuid1, "4")); + beforeAndAfterFlush(() -> { + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k='0' AND c={" + uuid1 + ": '0', " + uuid2 + ": '1'}"), 1); + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k='0' AND c={" + uuid2 + ": '1', " + uuid1 + ": '0'}"), 1); + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k='0' AND c={" + uuid1 + ": '4', " + uuid2 + ": '3'}"), 1); + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k='0' AND c={" + uuid2 + ": '3', " + uuid1 + ": '4'}"), 1); + }); + } + + + @Test + public void testSelectingMapDataWithParameterizedQueriesIsKeyOrderIndependent() throws Throwable + { + UUID uuid1 = UUIDs.timeBased(); + UUID uuid2 = UUIDs.timeBased(); + createTable("CREATE TABLE %s (k text, c frozen<map<timeuuid, text>>, PRIMARY KEY (k, c));"); + executeNet("INSERT INTO %s (k, c) VALUES ('0', {" + uuid1 + ": '0', " + uuid2 + ": '1'})"); + executeNet("INSERT INTO %s (k, c) VALUES ('0', {" + uuid2 + ": '3', " + uuid1 + ": '4'})"); + beforeAndAfterFlush(() -> { + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k=? AND c=?", "0", linkedHashMap(uuid1, "0", uuid2, "1")), 1); + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k=? AND c=?", "0", linkedHashMap(uuid2, "1", uuid1, "0")), 1); + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k=? AND c=?", "0", linkedHashMap(uuid1, "4", uuid2, "3")), 1); + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k=? AND c=?", "0", linkedHashMap(uuid2, "3", uuid1, "4")), 1); + }); + } + + @Test + public void testInsertingSetDataWithParameterizedQueriesIsKeyOrderIndependent() throws Throwable + { + UUID uuid1 = UUIDs.timeBased(); + UUID uuid2 = UUIDs.timeBased(); + createTable("CREATE TABLE %s (k text, c frozen<set<timeuuid>>, PRIMARY KEY (k, c));"); + executeNet("INSERT INTO %s (k, c) VALUES ('0', ?)", linkedHashSet(uuid1, uuid2)); + executeNet("INSERT INTO %s (k, c) VALUES ('0', ?)", linkedHashSet(uuid2, uuid1)); + beforeAndAfterFlush(() -> { + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k='0' AND c={" + uuid1 + ", " + uuid2 + '}'), 1); + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k='0' AND c={" + uuid2 + ", " + uuid1 + '}'), 1); + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k='0' AND c={" + uuid1 + ", " + uuid2 + '}'), 1); + assertRowCountNet(executeNet("SELECT * FROM %s WHERE k='0' AND c={" + uuid2 + ", " + uuid1 + '}'), 1); + }); + } + + + @Test + public void testSelectingSetDataWithParameterizedQueriesIsKeyOrderIndependent() throws Throwable + { + UUID uuid1 = UUIDs.timeBased(); + UUID uuid2 = UUIDs.timeBased(); + createTable("CREATE TABLE %s (k text, c frozen<set<timeuuid>>, PRIMARY KEY (k, c));"); + executeNet("INSERT INTO %s (k, c) VALUES ('0', {" + uuid1 + ", " + uuid2 + "})"); + beforeAndAfterFlush(() -> { + assertRowsNet(executeNet("SELECT k, c from %s where k='0' and c=?", linkedHashSet(uuid1, uuid2)), row("0", list(uuid1, uuid2))); + assertRowsNet(executeNet("SELECT k, c from %s where k='0' and c=?", linkedHashSet(uuid2, uuid1)), row("0", list(uuid1, uuid2))); + }); + } + // End tests for CASSANDRA-17623 } diff --git a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java index 7d79036ba1..f66b66f173 100644 --- a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java @@ -22,8 +22,13 @@ import java.io.FilenameFilter; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import com.google.common.collect.ImmutableMap; import com.google.common.io.Files; @@ -32,6 +37,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import com.datastax.driver.core.utils.UUIDs; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; import org.apache.cassandra.config.CFMetaData; @@ -41,6 +47,7 @@ import org.apache.cassandra.config.Schema; import org.apache.cassandra.cql3.QueryProcessor; import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.db.Keyspace; +import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.dht.*; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.ByteBufferUtil; @@ -49,6 +56,7 @@ import org.apache.cassandra.utils.OutputHandler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class CQLSSTableWriterTest @@ -398,6 +406,190 @@ public class CQLSSTableWriterTest assertEquals(threads.length * NUMBER_WRITES_IN_RUNNABLE, rs.size()); } + @Test + public void testFrozenMapType() throws Exception + { + final String KS = "cql_keyspace3"; + final String TABLE = "table3"; + final String qualifiedTable = KS + "." + TABLE; + File tempdir = Files.createTempDir(); + File dataDir = new File(tempdir.getAbsolutePath() + File.separator + KS + File.separator + TABLE); + assert dataDir.mkdirs(); + // Test to make sure we can write to `date` fields in both old and new formats + String schema = "CREATE TABLE " + qualifiedTable + " (" + + " k text," + + " c frozen<map<text, text>>," + + " PRIMARY KEY (k, c)" + + ")"; + String insert = "INSERT INTO " + qualifiedTable + " (k, c) VALUES (?, ?)"; + CQLSSTableWriter writer = CQLSSTableWriter.builder() + .inDirectory(dataDir) + .forTable(schema) + .using(insert) + .withBufferSizeInMB(1) + .build(); + for (int i = 0; i < 100; i++) + { + LinkedHashMap<String, String> map = new LinkedHashMap<>(); + map.put("a_key", "av" + i); + map.put("b_key", "zv" + i); + writer.addRow(String.valueOf(i), map); + } + for (int i = 100; i < 200; i++) + { + LinkedHashMap<String, String> map = new LinkedHashMap<>(); + map.put("b_key", "zv" + i); + map.put("a_key", "av" + i); + writer.addRow(String.valueOf(i), map); + } + writer.close(); + loadSSTables(dataDir, KS); + + UntypedResultSet rs = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + ";"); + assertEquals(200, rs.size()); + Map<String, Map<String, String>> map = StreamSupport.stream(rs.spliterator(), false) + .collect(Collectors.toMap(r -> r.getString("k"), r -> r.getFrozenMap("c", UTF8Type.instance, UTF8Type.instance))); + for (int i = 0; i < 200; i++) + { + final String expectedKey = String.valueOf(i); + assertTrue(map.containsKey(expectedKey)); + Map<String, String> innerMap = map.get(expectedKey); + assertTrue(innerMap.containsKey("a_key")); + assertEquals(innerMap.get("a_key"), "av" + i); + assertTrue(innerMap.containsKey("b_key")); + assertEquals(innerMap.get("b_key"), "zv" + i); + } + + // Make sure we can filter with map values regardless of which order we put the keys in + UntypedResultSet filtered; + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='0' and c={'a_key': 'av0', 'b_key': 'zv0'};"); + assertEquals(1, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='0' and c={'b_key': 'zv0', 'a_key': 'av0'};"); + assertEquals(1, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='100' and c={'b_key': 'zv100', 'a_key': 'av100'};"); + assertEquals(1, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='100' and c={'a_key': 'av100', 'b_key': 'zv100'};"); + assertEquals(1, filtered.size()); + } + + @Test + public void testFrozenMapTypeCustomOrdered() throws Exception + { + final String KS = "cql_keyspace4"; + final String TABLE = "table4"; + final String qualifiedTable = KS + "." + TABLE; + File tempdir = Files.createTempDir(); + File dataDir = new File(tempdir.getAbsolutePath() + File.separator + KS + File.separator + TABLE); + assert dataDir.mkdirs(); + // Test to make sure we can write to `date` fields in both old and new formats + String schema = "CREATE TABLE " + qualifiedTable + " (" + + " k text," + + " c frozen<map<timeuuid, int>>," + + " PRIMARY KEY (k, c)" + + ")"; + String insert = "INSERT INTO " + qualifiedTable + " (k, c) VALUES (?, ?)"; + CQLSSTableWriter writer = CQLSSTableWriter.builder() + .inDirectory(dataDir) + .forTable(schema) + .using(insert) + .withBufferSizeInMB(1) + .build(); + UUID uuid1 = UUIDs.timeBased(); + UUID uuid2 = UUIDs.timeBased(); + UUID uuid3 = UUIDs.timeBased(); + UUID uuid4 = UUIDs.timeBased(); + Map<UUID, Integer> map = new LinkedHashMap<>(); + // NOTE: if these two `put` calls are switched, the test passes + map.put(uuid2, 2); + map.put(uuid1, 1); + writer.addRow(String.valueOf(1), map); + + Map<UUID, Integer> map2 = new LinkedHashMap<>(); + map2.put(uuid3, 1); + map2.put(uuid4, 2); + writer.addRow(String.valueOf(2), map2); + + writer.close(); + loadSSTables(dataDir, KS); + + UntypedResultSet rs = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + ";"); + assertEquals(2, rs.size()); + + // Make sure we can filter with map values regardless of which order we put the keys in + UntypedResultSet filtered; + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='1' and c={" + uuid1 + ": 1, " + uuid2 + ": 2};"); + assertEquals(1, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='1' and c={" + uuid2 + ": 2, " + uuid1 + ": 1};"); + assertEquals(1, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='2' and c={" + uuid3 + ": 1, " + uuid4 + ": 2};"); + assertEquals(1, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='2' and c={" + uuid4 + ": 2, " + uuid3 + ": 1};"); + assertEquals(1, filtered.size()); + UUID other = UUIDs.startOf(1234L); // Just some other TimeUUID + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='2' and c={" + uuid3 + ": 1, " + other + ": 2};"); + assertEquals(0, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='2' and c={" + uuid4 + ": 2, " + other + ": 1};"); + assertEquals(0, filtered.size()); + } + + @Test + public void testFrozenSetTypeCustomOrdered() throws Exception + { + final String KS = "cql_keyspace5"; + final String TABLE = "table5"; + final String qualifiedTable = KS + "." + TABLE; + File tempdir = Files.createTempDir(); + File dataDir = new File(tempdir.getAbsolutePath() + File.separator + KS + File.separator + TABLE); + assert dataDir.mkdirs(); + // Test to make sure we can write to `date` fields in both old and new formats + String schema = "CREATE TABLE " + qualifiedTable + " (" + + " k text," + + " c frozen<set<timeuuid>>," + + " PRIMARY KEY (k, c)" + + ")"; + String insert = "INSERT INTO " + qualifiedTable + " (k, c) VALUES (?, ?)"; + CQLSSTableWriter writer = CQLSSTableWriter.builder() + .inDirectory(dataDir) + .forTable(schema) + .using(insert) + .withBufferSizeInMB(1) + .build(); + UUID uuid1 = UUIDs.startOf(0L); + UUID uuid2 = UUIDs.startOf(10000000L); + + LinkedHashSet<UUID> set = new LinkedHashSet<>(); + set.add(uuid1); + set.add(uuid2); + writer.addRow(String.valueOf(1), set); + + LinkedHashSet<UUID> set2 = new LinkedHashSet<>(); + set2.add(uuid2); + set2.add(uuid1); + writer.addRow(String.valueOf(2), set2); + + writer.close(); + loadSSTables(dataDir, KS); + + UntypedResultSet rs = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + ";"); + assertEquals(2, rs.size()); + + // Make sure we can filter with map values regardless of which order we put the keys in + UntypedResultSet filtered; + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='1' and c={" + uuid1 + ", " + uuid2 + "};"); + assertEquals(1, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='1' and c={" + uuid2 + ", " + uuid1 + "};"); + assertEquals(1, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='2' and c={" + uuid1 + ", " + uuid2 + "};"); + assertEquals(1, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='2' and c={" + uuid2 + ", " + uuid1 + "};"); + assertEquals(1, filtered.size()); + UUID other = UUIDs.startOf(10000000L + 1L); // Pick one that's really close just to make sure clustering filters are working + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='2' and c={" + uuid1 + ", " + other + "};"); + assertEquals(0, filtered.size()); + filtered = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + " where k='2' and c={" + other + ", " + uuid1 + "};"); + assertEquals(0, filtered.size()); + } + private static void loadSSTables(File dataDir, String ks) throws ExecutionException, InterruptedException { SSTableLoader loader = new SSTableLoader(dataDir, new SSTableLoader.Client() --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org