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

Reply via email to