This is an automated email from the ASF dual-hosted git repository. adelapena pushed a commit to branch cassandra-3.11 in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit 0e1d068d9237de39b53992522eba4b40afb0e72b Merge: 01ebd9936f ffc4c89c3d Author: Andrés de la Peña <a.penya.gar...@gmail.com> AuthorDate: Fri May 20 11:43:56 2022 +0100 Merge branch 'cassandra-3.0' into cassandra-3.11 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 | 171 +++++++++++++++++++++ 7 files changed, 282 insertions(+), 8 deletions(-) diff --cc CHANGES.txt index 689fbfd2ba,c49c33224f..d796eb9449 --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,5 -1,5 +1,6 @@@ -3.0.28 +3.11.14 +Merged from 3.0: + * 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 --cc test/unit/org/apache/cassandra/cql3/CQLTester.java index 705fad8f1f,fe8ed5b9c3..996cfc8d5e --- a/test/unit/org/apache/cassandra/cql3/CQLTester.java +++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java @@@ -31,7 -31,10 +31,9 @@@ import java.util.concurrent.atomic.Atom 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; @@@ -801,7 -797,11 +803,11 @@@ public abstract class CQLTeste 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 + protected com.datastax.driver.core.ResultSet executeNet(ProtocolVersion protocolVersion, String query, Object... values) throws Throwable { return sessionNet(protocolVersion).execute(formatQuery(query), values); } @@@ -952,6 -942,18 +958,13 @@@ rows.length>i ? "less" : "more", rows.length, i, protocolVersion), i == rows.length); } - protected void assertRowsNet(ResultSet result, Object[]... rows) - { - 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) diff --cc test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java index dbeefbb7da,f66b66f173..57910fbf6f --- a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java @@@ -19,13 -19,17 +19,15 @@@ package org.apache.cassandra.io.sstable import java.io.File; import java.io.FilenameFilter; +import java.io.IOException; 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.*; import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + import java.util.stream.Collectors; + import java.util.stream.StreamSupport; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.io.Files; @@@ -35,21 -37,22 +37,23 @@@ import org.junit.Before 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; -import org.apache.cassandra.config.Config; -import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.config.Schema; -import org.apache.cassandra.cql3.QueryProcessor; -import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.config.*; +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.cql3.functions.UDHelper; import org.apache.cassandra.db.Keyspace; + import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.dht.*; +import org.apache.cassandra.exceptions.*; import org.apache.cassandra.service.StorageService; -import org.apache.cassandra.utils.ByteBufferUtil; -import org.apache.cassandra.utils.FBUtilities; -import org.apache.cassandra.utils.OutputHandler; +import org.apache.cassandra.utils.*; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ProtocolVersion; +import com.datastax.driver.core.TypeCodec; +import com.datastax.driver.core.UDTValue; +import com.datastax.driver.core.UserType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@@ -607,38 -330,266 +612,204 @@@ public class CQLSSTableWriterTes assertFalse(iter.hasNext()); } - private static final int NUMBER_WRITES_IN_RUNNABLE = 10; - private class WriterThread extends Thread - { - private final File dataDir; - private final int id; - public volatile Exception exception; - - public WriterThread(File dataDir, int id) - { - this.dataDir = dataDir; - this.id = id; - } - - @Override - public void run() - { - String schema = "CREATE TABLE cql_keyspace2.table2 (" - + " k int," - + " v int," - + " PRIMARY KEY (k, v)" - + ")"; - String insert = "INSERT INTO cql_keyspace2.table2 (k, v) VALUES (?, ?)"; - CQLSSTableWriter writer = CQLSSTableWriter.builder() - .inDirectory(dataDir) - .forTable(schema) - .using(insert).build(); - - try - { - for (int i = 0; i < NUMBER_WRITES_IN_RUNNABLE; i++) - { - writer.addRow(id, i); - } - writer.close(); - } - catch (Exception e) - { - exception = e; - } - } - } - @Test - public void testConcurrentWriters() throws Exception + public void testWriteWithNestedTupleUdt() throws Exception { - final String KS = "cql_keyspace2"; - final String TABLE = "table2"; - - File tempdir = Files.createTempDir(); - File dataDir = new File(tempdir.getAbsolutePath() + File.separator + KS + File.separator + TABLE); - assert dataDir.mkdirs(); + // Check the writer does not throw "InvalidRequestException: Non-frozen tuples are not allowed inside collections: list<tuple<int, int>>" + // See CASSANDRA-15857 + final String schema = "CREATE TABLE " + qualifiedTable + " (" + + " k int," + + " v1 frozen<nested_type>," + + " PRIMARY KEY (k)" + + ")"; - WriterThread[] threads = new WriterThread[5]; - for (int i = 0; i < threads.length; i++) - { - WriterThread thread = new WriterThread(dataDir, i); - threads[i] = thread; - thread.start(); - } + CQLSSTableWriter writer = CQLSSTableWriter.builder() + .inDirectory(dataDir) + .withType("CREATE TYPE " + keyspace + ".nested_type (a list<tuple<int, int>>)") + .forTable(schema) + .using("INSERT INTO " + qualifiedTable + " (k, v1) " + + "VALUES (?, ?)").build(); - for (WriterThread thread : threads) + UserType nestedType = writer.getUDType("nested_type"); + for (int i = 0; i < 100; i++) { - thread.join(); - assert !thread.isAlive() : "Thread should be dead by now"; - if (thread.exception != null) - { - throw thread.exception; - } + writer.addRow(i, nestedType.newValue() + .setList("a", Collections.emptyList())); } - loadSSTables(dataDir, KS); + writer.close(); + loadSSTables(dataDir, keyspace); - UntypedResultSet rs = QueryProcessor.executeInternal("SELECT * FROM cql_keyspace2.table2;"); - assertEquals(threads.length * NUMBER_WRITES_IN_RUNNABLE, rs.size()); + UntypedResultSet resultSet = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable); + assertEquals(100, resultSet.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); ++ loadSSTables(dataDir, keyspace); + + 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); ++ loadSSTables(dataDir, keyspace); + + 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); ++ loadSSTables(dataDir, keyspace); + + 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