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 2022/05/20 10:59:19 UTC
[cassandra] branch cassandra-3.0 updated: Fix issue where frozen maps may not be serialized in the correct order
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 <dr...@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 <dr...@apple.com>
Co-authored-by: Francisco Guerrero <fr...@gmail.com>
Co-authored-by: Yifan Cai <yc...@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-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org