This is an automated email from the ASF dual-hosted git repository. adelapena pushed a commit to branch cassandra-4.0 in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit 5cf6db30f6b907e573c20b4f7b2958fb7e8995e1 Merge: af24242af9 0e1d068d92 Author: Andrés de la Peña <a.penya.gar...@gmail.com> AuthorDate: Fri May 20 11:47:06 2022 +0100 Merge branch 'cassandra-3.11' into cassandra-4.0 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 | 31 +++- .../cql3/conditions/ColumnConditionTest.java | 6 +- .../cql3/validation/entities/CollectionsTest.java | 99 ++++++++++++ .../cassandra/io/sstable/CQLSSTableWriterTest.java | 171 ++++++++++++++++++++- 7 files changed, 307 insertions(+), 14 deletions(-) diff --cc CHANGES.txt index bf7f8137c6,d796eb9449..6c31e17981 --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,6 -1,6 +1,7 @@@ -3.11.14 +4.0.5 +Merged from 3.11: 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 src/java/org/apache/cassandra/cql3/Maps.java index 6e7e07b576,e21fc6c5c2..3a9575c833 --- a/src/java/org/apache/cassandra/cql3/Maps.java +++ b/src/java/org/apache/cassandra/cql3/Maps.java @@@ -228,8 -184,9 +228,9 @@@ public abstract class Map { // 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<?, ?> m = type.getSerializer().deserializeForNativeProtocol(value, ByteBufferAccessor.instance, 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); diff --cc test/unit/org/apache/cassandra/cql3/CQLTester.java index 8d0adbfe7c,996cfc8d5e..a8ca1e5d24 --- a/test/unit/org/apache/cassandra/cql3/CQLTester.java +++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java @@@ -31,21 -28,12 +31,23 @@@ import java.util.* import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.rmi.RMIConnectorServer; + import com.google.common.base.Objects; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; + import com.google.common.collect.Iterables; + import org.junit.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@@ -218,39 -205,45 +220,39 @@@ public abstract class CQLTeste public static void cleanup() { - // clean up commitlog - String[] directoryNames = { DatabaseDescriptor.getCommitLogLocation(), }; - for (String dirName : directoryNames) - { - File dir = new File(dirName); - if (!dir.exists()) - throw new RuntimeException("No such directory: " + dir.getAbsolutePath()); - FileUtils.deleteRecursive(dir); - } - - File cdcDir = new File(DatabaseDescriptor.getCDCLogLocation()); - if (cdcDir.exists()) - FileUtils.deleteRecursive(cdcDir); + ServerTestUtils.cleanup(); + } - cleanupSavedCaches(); + /** + * Starts the JMX server. It's safe to call this method multiple times. + */ + public static void startJMXServer() throws Exception + { + if (jmxServer != null) + return; - // clean up data directory which are stored as data directory/keyspace/data files - for (String dirName : DatabaseDescriptor.getAllDataFileLocations()) - { - File dir = new File(dirName); - if (!dir.exists()) - throw new RuntimeException("No such directory: " + dir.getAbsolutePath()); - FileUtils.deleteRecursive(dir); - } + InetAddress loopback = InetAddress.getLoopbackAddress(); + jmxHost = loopback.getHostAddress(); + jmxPort = getAutomaticallyAllocatedPort(loopback); + jmxServer = JMXServerUtils.createJMXServer(jmxPort, true); + jmxServer.start(); } - + - public static void mkdirs() + public static void createMBeanServerConnection() throws Exception { - DatabaseDescriptor.createAllDirectories(); + assert jmxServer != null : "jmxServer not started"; + + Map<String, Object> env = new HashMap<>(); + env.put("com.sun.jndi.rmi.factory.socket", RMISocketFactory.getDefaultSocketFactory()); + JMXConnector jmxc = JMXConnectorFactory.connect(getJMXServiceURL(), env); + jmxConnection = jmxc.getMBeanServerConnection(); } - public static void cleanupSavedCaches() + public static JMXServiceURL getJMXServiceURL() throws MalformedURLException { - File cachesDir = new File(DatabaseDescriptor.getSavedCachesLocation()); + assert jmxServer != null : "jmxServer not started"; - if (!cachesDir.exists() || !cachesDir.isDirectory()) - return; - - FileUtils.delete(cachesDir.listFiles()); + return new JMXServiceURL(String.format("service:jmx:rmi:///jndi/rmi://%s:%d/jmxrmi", jmxHost, jmxPort)); } @BeforeClass @@@ -731,23 -643,9 +733,23 @@@ return currentKeyspace; } + protected void alterKeyspace(String query) + { + String fullQuery = String.format(query, currentKeyspace()); + logger.info(fullQuery); + schemaChange(fullQuery); + } - ++ + protected void alterKeyspaceMayThrow(String query) throws Throwable + { + String fullQuery = String.format(query, currentKeyspace()); + logger.info(fullQuery); + QueryProcessor.executeOnceInternal(fullQuery); + } - ++ protected String createKeyspaceName() { - String currentKeyspace = "keyspace_" + seqNumber.getAndIncrement(); + String currentKeyspace = String.format("keyspace_%02d", seqNumber.getAndIncrement()); keyspaces.add(currentKeyspace); return currentKeyspace; } @@@ -1788,13 -1612,27 +1797,27 @@@ 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(); + throw new IllegalArgumentException("Invalid number of arguments, got " + values.length); int size = values.length / 2; - Map<Object, Object> 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 --cc test/unit/org/apache/cassandra/cql3/conditions/ColumnConditionTest.java index 0035fa161d,0000000000..f62119d4e6 mode 100644,000000..100644 --- a/test/unit/org/apache/cassandra/cql3/conditions/ColumnConditionTest.java +++ b/test/unit/org/apache/cassandra/cql3/conditions/ColumnConditionTest.java @@@ -1,555 -1,0 +1,555 @@@ +/* + * 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.conditions; + +import java.nio.ByteBuffer; +import java.util.*; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.cassandra.cql3.*; +import org.apache.cassandra.db.Clustering; +import org.apache.cassandra.db.marshal.Int32Type; +import org.apache.cassandra.db.marshal.ListType; +import org.apache.cassandra.db.marshal.MapType; +import org.apache.cassandra.db.marshal.SetType; +import org.apache.cassandra.db.rows.*; +import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.schema.ColumnMetadata; +import org.apache.cassandra.serializers.TimeUUIDSerializer; +import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.cassandra.utils.UUIDGen; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import static org.apache.cassandra.cql3.Operator.*; +import static org.apache.cassandra.utils.ByteBufferUtil.EMPTY_BYTE_BUFFER; + + +public class ColumnConditionTest +{ + public static final ByteBuffer ZERO = Int32Type.instance.fromString("0"); + public static final ByteBuffer ONE = Int32Type.instance.fromString("1"); + public static final ByteBuffer TWO = Int32Type.instance.fromString("2"); + + private static Row newRow(ColumnMetadata definition, ByteBuffer value) + { + BufferCell cell = new BufferCell(definition, 0L, Cell.NO_TTL, Cell.NO_DELETION_TIME, value, null); + return BTreeRow.singleCellRow(Clustering.EMPTY, cell); + } + + private static Row newRow(ColumnMetadata definition, List<ByteBuffer> values) + { + Row.Builder builder = BTreeRow.sortedBuilder(); + builder.newRow(Clustering.EMPTY); + long now = System.currentTimeMillis(); + if (values != null) + { + for (int i = 0, m = values.size(); i < m; i++) + { + UUID uuid = UUIDGen.getTimeUUID(now, i); + ByteBuffer key = TimeUUIDSerializer.instance.serialize(uuid); + ByteBuffer value = values.get(i); + BufferCell cell = new BufferCell(definition, + 0L, + Cell.NO_TTL, + Cell.NO_DELETION_TIME, + value, + CellPath.create(key)); + builder.addCell(cell); + } + } + return builder.build(); + } + + private static Row newRow(ColumnMetadata definition, SortedSet<ByteBuffer> values) + { + Row.Builder builder = BTreeRow.sortedBuilder(); + builder.newRow(Clustering.EMPTY); + if (values != null) + { + for (ByteBuffer value : values) + { + BufferCell cell = new BufferCell(definition, + 0L, + Cell.NO_TTL, + Cell.NO_DELETION_TIME, + ByteBufferUtil.EMPTY_BYTE_BUFFER, + CellPath.create(value)); + builder.addCell(cell); + } + } + return builder.build(); + } + + private static Row newRow(ColumnMetadata definition, Map<ByteBuffer, ByteBuffer> values) + { + Row.Builder builder = BTreeRow.sortedBuilder(); + builder.newRow(Clustering.EMPTY); + if (values != null) + { + for (Map.Entry<ByteBuffer, ByteBuffer> entry : values.entrySet()) + { + BufferCell cell = new BufferCell(definition, + 0L, + Cell.NO_TTL, + Cell.NO_DELETION_TIME, + entry.getValue(), + CellPath.create(entry.getKey())); + builder.addCell(cell); + } + } + return builder.build(); + } + + private static boolean conditionApplies(ByteBuffer rowValue, Operator op, ByteBuffer conditionValue) + { + ColumnMetadata definition = ColumnMetadata.regularColumn("ks", "cf", "c", Int32Type.instance); + ColumnCondition condition = ColumnCondition.condition(definition, op, Terms.of(new Constants.Value(conditionValue))); + ColumnCondition.Bound bound = condition.bind(QueryOptions.DEFAULT); + return bound.appliesTo(newRow(definition, rowValue)); + } + + private static boolean conditionApplies(List<ByteBuffer> rowValue, Operator op, List<ByteBuffer> conditionValue) + { + ColumnMetadata definition = ColumnMetadata.regularColumn("ks", "cf", "c", ListType.getInstance(Int32Type.instance, true)); + ColumnCondition condition = ColumnCondition.condition(definition, op, Terms.of(new Lists.Value(conditionValue))); + ColumnCondition.Bound bound = condition.bind(QueryOptions.DEFAULT); + return bound.appliesTo(newRow(definition, rowValue)); + } + + private static boolean conditionApplies(SortedSet<ByteBuffer> rowValue, Operator op, SortedSet<ByteBuffer> conditionValue) + { + ColumnMetadata definition = ColumnMetadata.regularColumn("ks", "cf", "c", SetType.getInstance(Int32Type.instance, true)); + ColumnCondition condition = ColumnCondition.condition(definition, op, Terms.of(new Sets.Value(conditionValue))); + ColumnCondition.Bound bound = condition.bind(QueryOptions.DEFAULT); + return bound.appliesTo(newRow(definition, rowValue)); + } + - private static boolean conditionApplies(Map<ByteBuffer, ByteBuffer> rowValue, Operator op, Map<ByteBuffer, ByteBuffer> conditionValue) ++ private static boolean conditionApplies(Map<ByteBuffer, ByteBuffer> rowValue, Operator op, SortedMap<ByteBuffer, ByteBuffer> conditionValue) + { + ColumnMetadata definition = ColumnMetadata.regularColumn("ks", "cf", "c", MapType.getInstance(Int32Type.instance, Int32Type.instance, true)); + ColumnCondition condition = ColumnCondition.condition(definition, op, Terms.of(new Maps.Value(conditionValue))); + ColumnCondition.Bound bound = condition.bind(QueryOptions.DEFAULT); + return bound.appliesTo(newRow(definition, rowValue)); + } + + @FunctionalInterface + public interface CheckedFunction { + void apply(); + } + + private static void assertThrowsIRE(CheckedFunction runnable, String errorMessage) + { + try + { + runnable.apply(); + fail("Expected InvalidRequestException was not thrown"); + } catch (InvalidRequestException e) + { + Assert.assertTrue("Expected error message to contain '" + errorMessage + "', but got '" + e.getMessage() + "'", + e.getMessage().contains(errorMessage)); + } + } + + @Test + public void testSimpleBoundIsSatisfiedByValue() throws InvalidRequestException + { + // EQ + assertTrue(conditionApplies(ONE, EQ, ONE)); + assertFalse(conditionApplies(TWO, EQ, ONE)); + assertFalse(conditionApplies(ONE, EQ, TWO)); + assertFalse(conditionApplies(ONE, EQ, EMPTY_BYTE_BUFFER)); + assertFalse(conditionApplies(EMPTY_BYTE_BUFFER, EQ, ONE)); + assertTrue(conditionApplies(EMPTY_BYTE_BUFFER, EQ, EMPTY_BYTE_BUFFER)); + assertFalse(conditionApplies(ONE, EQ, null)); + assertFalse(conditionApplies(null, EQ, ONE)); + assertTrue(conditionApplies((ByteBuffer) null, EQ, (ByteBuffer) null)); + + // NEQ + assertFalse(conditionApplies(ONE, NEQ, ONE)); + assertTrue(conditionApplies(TWO, NEQ, ONE)); + assertTrue(conditionApplies(ONE, NEQ, TWO)); + assertTrue(conditionApplies(ONE, NEQ, EMPTY_BYTE_BUFFER)); + assertTrue(conditionApplies(EMPTY_BYTE_BUFFER, NEQ, ONE)); + assertFalse(conditionApplies(EMPTY_BYTE_BUFFER, NEQ, EMPTY_BYTE_BUFFER)); + assertTrue(conditionApplies(ONE, NEQ, null)); + assertTrue(conditionApplies(null, NEQ, ONE)); + assertFalse(conditionApplies((ByteBuffer) null, NEQ, (ByteBuffer) null)); + + // LT + assertFalse(conditionApplies(ONE, LT, ONE)); + assertFalse(conditionApplies(TWO, LT, ONE)); + assertTrue(conditionApplies(ONE, LT, TWO)); + assertFalse(conditionApplies(ONE, LT, EMPTY_BYTE_BUFFER)); + assertTrue(conditionApplies(EMPTY_BYTE_BUFFER, LT, ONE)); + assertFalse(conditionApplies(EMPTY_BYTE_BUFFER, LT, EMPTY_BYTE_BUFFER)); + assertThrowsIRE(() -> conditionApplies(ONE, LT, null), "Invalid comparison with null for operator \"<\""); + assertFalse(conditionApplies(null, LT, ONE)); + + // LTE + assertTrue(conditionApplies(ONE, LTE, ONE)); + assertFalse(conditionApplies(TWO, LTE, ONE)); + assertTrue(conditionApplies(ONE, LTE, TWO)); + assertFalse(conditionApplies(ONE, LTE, EMPTY_BYTE_BUFFER)); + assertTrue(conditionApplies(EMPTY_BYTE_BUFFER, LTE, ONE)); + assertTrue(conditionApplies(EMPTY_BYTE_BUFFER, LTE, EMPTY_BYTE_BUFFER)); + assertThrowsIRE(() -> conditionApplies(ONE, LTE, null), "Invalid comparison with null for operator \"<=\""); + assertFalse(conditionApplies(null, LTE, ONE)); + + // GT + assertFalse(conditionApplies(ONE, GT, ONE)); + assertTrue(conditionApplies(TWO, GT, ONE)); + assertFalse(conditionApplies(ONE, GT, TWO)); + assertTrue(conditionApplies(ONE, GT, EMPTY_BYTE_BUFFER)); + assertFalse(conditionApplies(EMPTY_BYTE_BUFFER, GT, ONE)); + assertFalse(conditionApplies(EMPTY_BYTE_BUFFER, GT, EMPTY_BYTE_BUFFER)); + assertThrowsIRE(() -> conditionApplies(ONE, GT, null), "Invalid comparison with null for operator \">\""); + assertFalse(conditionApplies(null, GT, ONE)); + + // GTE + assertTrue(conditionApplies(ONE, GTE, ONE)); + assertTrue(conditionApplies(TWO, GTE, ONE)); + assertFalse(conditionApplies(ONE, GTE, TWO)); + assertTrue(conditionApplies(ONE, GTE, EMPTY_BYTE_BUFFER)); + assertFalse(conditionApplies(EMPTY_BYTE_BUFFER, GTE, ONE)); + assertTrue(conditionApplies(EMPTY_BYTE_BUFFER, GTE, EMPTY_BYTE_BUFFER)); + assertThrowsIRE(() -> conditionApplies(ONE, GTE, null), "Invalid comparison with null for operator \">=\""); + assertFalse(conditionApplies(null, GTE, ONE)); + } + + private static List<ByteBuffer> list(ByteBuffer... values) + { + return Arrays.asList(values); + } + + @Test + // sets use the same check as lists + public void testListCollectionBoundAppliesTo() throws InvalidRequestException + { + // EQ + assertTrue(conditionApplies(list(ONE), EQ, list(ONE))); + assertTrue(conditionApplies(list(), EQ, list())); + assertFalse(conditionApplies(list(ONE), EQ, list(ZERO))); + assertFalse(conditionApplies(list(ZERO), EQ, list(ONE))); + assertFalse(conditionApplies(list(ONE, ONE), EQ, list(ONE))); + assertFalse(conditionApplies(list(ONE), EQ, list(ONE, ONE))); + assertFalse(conditionApplies(list(ONE), EQ, list())); + assertFalse(conditionApplies(list(), EQ, list(ONE))); + + assertFalse(conditionApplies(list(ONE), EQ, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertFalse(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), EQ, list(ONE))); + assertTrue(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), EQ, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // NEQ + assertFalse(conditionApplies(list(ONE), NEQ, list(ONE))); + assertFalse(conditionApplies(list(), NEQ, list())); + assertTrue(conditionApplies(list(ONE), NEQ, list(ZERO))); + assertTrue(conditionApplies(list(ZERO), NEQ, list(ONE))); + assertTrue(conditionApplies(list(ONE, ONE), NEQ, list(ONE))); + assertTrue(conditionApplies(list(ONE), NEQ, list(ONE, ONE))); + assertTrue(conditionApplies(list(ONE), NEQ, list())); + assertTrue(conditionApplies(list(), NEQ, list(ONE))); + + assertTrue(conditionApplies(list(ONE), NEQ, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertTrue(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), NEQ, list(ONE))); + assertFalse(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), NEQ, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // LT + assertFalse(conditionApplies(list(ONE), LT, list(ONE))); + assertFalse(conditionApplies(list(), LT, list())); + assertFalse(conditionApplies(list(ONE), LT, list(ZERO))); + assertTrue(conditionApplies(list(ZERO), LT, list(ONE))); + assertFalse(conditionApplies(list(ONE, ONE), LT, list(ONE))); + assertTrue(conditionApplies(list(ONE), LT, list(ONE, ONE))); + assertFalse(conditionApplies(list(ONE), LT, list())); + assertTrue(conditionApplies(list(), LT, list(ONE))); + + assertFalse(conditionApplies(list(ONE), LT, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertTrue(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), LT, list(ONE))); + assertFalse(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), LT, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // LTE + assertTrue(conditionApplies(list(ONE), LTE, list(ONE))); + assertTrue(conditionApplies(list(), LTE, list())); + assertFalse(conditionApplies(list(ONE), LTE, list(ZERO))); + assertTrue(conditionApplies(list(ZERO), LTE, list(ONE))); + assertFalse(conditionApplies(list(ONE, ONE), LTE, list(ONE))); + assertTrue(conditionApplies(list(ONE), LTE, list(ONE, ONE))); + assertFalse(conditionApplies(list(ONE), LTE, list())); + assertTrue(conditionApplies(list(), LTE, list(ONE))); + + assertFalse(conditionApplies(list(ONE), LTE, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertTrue(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), LTE, list(ONE))); + assertTrue(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), LTE, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // GT + assertFalse(conditionApplies(list(ONE), GT, list(ONE))); + assertFalse(conditionApplies(list(), GT, list())); + assertTrue(conditionApplies(list(ONE), GT, list(ZERO))); + assertFalse(conditionApplies(list(ZERO), GT, list(ONE))); + assertTrue(conditionApplies(list(ONE, ONE), GT, list(ONE))); + assertFalse(conditionApplies(list(ONE), GT, list(ONE, ONE))); + assertTrue(conditionApplies(list(ONE), GT, list())); + assertFalse(conditionApplies(list(), GT, list(ONE))); + + assertTrue(conditionApplies(list(ONE), GT, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertFalse(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), GT, list(ONE))); + assertFalse(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), GT, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // GTE + assertTrue(conditionApplies(list(ONE), GTE, list(ONE))); + assertTrue(conditionApplies(list(), GTE, list())); + assertTrue(conditionApplies(list(ONE), GTE, list(ZERO))); + assertFalse(conditionApplies(list(ZERO), GTE, list(ONE))); + assertTrue(conditionApplies(list(ONE, ONE), GTE, list(ONE))); + assertFalse(conditionApplies(list(ONE), GTE, list(ONE, ONE))); + assertTrue(conditionApplies(list(ONE), GTE, list())); + assertFalse(conditionApplies(list(), GTE, list(ONE))); + + assertTrue(conditionApplies(list(ONE), GTE, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertFalse(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), GTE, list(ONE))); + assertTrue(conditionApplies(list(ByteBufferUtil.EMPTY_BYTE_BUFFER), GTE, list(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + } + + private static SortedSet<ByteBuffer> set(ByteBuffer... values) + { + SortedSet<ByteBuffer> results = new TreeSet<>(Int32Type.instance); + results.addAll(Arrays.asList(values)); + return results; + } + + @Test + public void testSetCollectionBoundAppliesTo() throws InvalidRequestException + { + // EQ + assertTrue(conditionApplies(set(ONE), EQ, set(ONE))); + assertTrue(conditionApplies(set(), EQ, set())); + assertFalse(conditionApplies(set(ONE), EQ, set(ZERO))); + assertFalse(conditionApplies(set(ZERO), EQ, set(ONE))); + assertFalse(conditionApplies(set(ONE, TWO), EQ, set(ONE))); + assertFalse(conditionApplies(set(ONE), EQ, set(ONE, TWO))); + assertFalse(conditionApplies(set(ONE), EQ, set())); + assertFalse(conditionApplies(set(), EQ, set(ONE))); + + assertFalse(conditionApplies(set(ONE), EQ, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertFalse(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), EQ, set(ONE))); + assertTrue(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), EQ, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // NEQ + assertFalse(conditionApplies(set(ONE), NEQ, set(ONE))); + assertFalse(conditionApplies(set(), NEQ, set())); + assertTrue(conditionApplies(set(ONE), NEQ, set(ZERO))); + assertTrue(conditionApplies(set(ZERO), NEQ, set(ONE))); + assertTrue(conditionApplies(set(ONE, TWO), NEQ, set(ONE))); + assertTrue(conditionApplies(set(ONE), NEQ, set(ONE, TWO))); + assertTrue(conditionApplies(set(ONE), NEQ, set())); + assertTrue(conditionApplies(set(), NEQ, set(ONE))); + + assertTrue(conditionApplies(set(ONE), NEQ, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertTrue(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), NEQ, set(ONE))); + assertFalse(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), NEQ, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // LT + assertFalse(conditionApplies(set(ONE), LT, set(ONE))); + assertFalse(conditionApplies(set(), LT, set())); + assertFalse(conditionApplies(set(ONE), LT, set(ZERO))); + assertTrue(conditionApplies(set(ZERO), LT, set(ONE))); + assertFalse(conditionApplies(set(ONE, TWO), LT, set(ONE))); + assertTrue(conditionApplies(set(ONE), LT, set(ONE, TWO))); + assertFalse(conditionApplies(set(ONE), LT, set())); + assertTrue(conditionApplies(set(), LT, set(ONE))); + + assertFalse(conditionApplies(set(ONE), LT, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertTrue(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), LT, set(ONE))); + assertFalse(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), LT, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // LTE + assertTrue(conditionApplies(set(ONE), LTE, set(ONE))); + assertTrue(conditionApplies(set(), LTE, set())); + assertFalse(conditionApplies(set(ONE), LTE, set(ZERO))); + assertTrue(conditionApplies(set(ZERO), LTE, set(ONE))); + assertFalse(conditionApplies(set(ONE, TWO), LTE, set(ONE))); + assertTrue(conditionApplies(set(ONE), LTE, set(ONE, TWO))); + assertFalse(conditionApplies(set(ONE), LTE, set())); + assertTrue(conditionApplies(set(), LTE, set(ONE))); + + assertFalse(conditionApplies(set(ONE), LTE, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertTrue(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), LTE, set(ONE))); + assertTrue(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), LTE, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // GT + assertFalse(conditionApplies(set(ONE), GT, set(ONE))); + assertFalse(conditionApplies(set(), GT, set())); + assertTrue(conditionApplies(set(ONE), GT, set(ZERO))); + assertFalse(conditionApplies(set(ZERO), GT, set(ONE))); + assertTrue(conditionApplies(set(ONE, TWO), GT, set(ONE))); + assertFalse(conditionApplies(set(ONE), GT, set(ONE, TWO))); + assertTrue(conditionApplies(set(ONE), GT, set())); + assertFalse(conditionApplies(set(), GT, set(ONE))); + + assertTrue(conditionApplies(set(ONE), GT, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertFalse(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), GT, set(ONE))); + assertFalse(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), GT, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // GTE + assertTrue(conditionApplies(set(ONE), GTE, set(ONE))); + assertTrue(conditionApplies(set(), GTE, set())); + assertTrue(conditionApplies(set(ONE), GTE, set(ZERO))); + assertFalse(conditionApplies(set(ZERO), GTE, set(ONE))); + assertTrue(conditionApplies(set(ONE, TWO), GTE, set(ONE))); + assertFalse(conditionApplies(set(ONE), GTE, set(ONE, TWO))); + assertTrue(conditionApplies(set(ONE), GTE, set())); + assertFalse(conditionApplies(set(), GTE, set(ONE))); + + assertTrue(conditionApplies(set(ONE), GTE, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertFalse(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), GTE, set(ONE))); + assertTrue(conditionApplies(set(ByteBufferUtil.EMPTY_BYTE_BUFFER), GTE, set(ByteBufferUtil.EMPTY_BYTE_BUFFER))); + } + + // values should be a list of key, value, key, value, ... - private static Map<ByteBuffer, ByteBuffer> map(ByteBuffer... values) ++ private static SortedMap<ByteBuffer, ByteBuffer> map(ByteBuffer... values) + { - Map<ByteBuffer, ByteBuffer> map = new TreeMap<>(); ++ SortedMap<ByteBuffer, ByteBuffer> map = new TreeMap<>(); + for (int i = 0; i < values.length; i += 2) + map.put(values[i], values[i + 1]); + + return map; + } + + @Test + public void testMapCollectionBoundIsSatisfiedByValue() throws InvalidRequestException + { + // EQ + assertTrue(conditionApplies(map(ONE, ONE), EQ, map(ONE, ONE))); + assertTrue(conditionApplies(map(), EQ, map())); + assertFalse(conditionApplies(map(ONE, ONE), EQ, map(ZERO, ONE))); + assertFalse(conditionApplies(map(ZERO, ONE), EQ, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), EQ, map(ONE, ZERO))); + assertFalse(conditionApplies(map(ONE, ZERO), EQ, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE, TWO, ONE), EQ, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), EQ, map(ONE, ONE, TWO, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), EQ, map())); + assertFalse(conditionApplies(map(), EQ, map(ONE, ONE))); + + assertFalse(conditionApplies(map(ONE, ONE), EQ, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertFalse(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), EQ, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), EQ, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertFalse(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), EQ, map(ONE, ONE))); + assertTrue(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), EQ, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertTrue(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), EQ, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // NEQ + assertFalse(conditionApplies(map(ONE, ONE), NEQ, map(ONE, ONE))); + assertFalse(conditionApplies(map(), NEQ, map())); + assertTrue(conditionApplies(map(ONE, ONE), NEQ, map(ZERO, ONE))); + assertTrue(conditionApplies(map(ZERO, ONE), NEQ, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), NEQ, map(ONE, ZERO))); + assertTrue(conditionApplies(map(ONE, ZERO), NEQ, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE, TWO, ONE), NEQ, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), NEQ, map(ONE, ONE, TWO, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), NEQ, map())); + assertTrue(conditionApplies(map(), NEQ, map(ONE, ONE))); + + assertTrue(conditionApplies(map(ONE, ONE), NEQ, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertTrue(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), NEQ, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), NEQ, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertTrue(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), NEQ, map(ONE, ONE))); + assertFalse(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), NEQ, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertFalse(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), NEQ, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // LT + assertFalse(conditionApplies(map(ONE, ONE), LT, map(ONE, ONE))); + assertFalse(conditionApplies(map(), LT, map())); + assertFalse(conditionApplies(map(ONE, ONE), LT, map(ZERO, ONE))); + assertTrue(conditionApplies(map(ZERO, ONE), LT, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), LT, map(ONE, ZERO))); + assertTrue(conditionApplies(map(ONE, ZERO), LT, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE, TWO, ONE), LT, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), LT, map(ONE, ONE, TWO, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), LT, map())); + assertTrue(conditionApplies(map(), LT, map(ONE, ONE))); + + assertFalse(conditionApplies(map(ONE, ONE), LT, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertTrue(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), LT, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), LT, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertTrue(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), LT, map(ONE, ONE))); + assertFalse(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), LT, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertFalse(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), LT, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // LTE + assertTrue(conditionApplies(map(ONE, ONE), LTE, map(ONE, ONE))); + assertTrue(conditionApplies(map(), LTE, map())); + assertFalse(conditionApplies(map(ONE, ONE), LTE, map(ZERO, ONE))); + assertTrue(conditionApplies(map(ZERO, ONE), LTE, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), LTE, map(ONE, ZERO))); + assertTrue(conditionApplies(map(ONE, ZERO), LTE, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE, TWO, ONE), LTE, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), LTE, map(ONE, ONE, TWO, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), LTE, map())); + assertTrue(conditionApplies(map(), LTE, map(ONE, ONE))); + + assertFalse(conditionApplies(map(ONE, ONE), LTE, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertTrue(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), LTE, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), LTE, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertTrue(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), LTE, map(ONE, ONE))); + assertTrue(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), LTE, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertTrue(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), LTE, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // GT + assertFalse(conditionApplies(map(ONE, ONE), GT, map(ONE, ONE))); + assertFalse(conditionApplies(map(), GT, map())); + assertTrue(conditionApplies(map(ONE, ONE), GT, map(ZERO, ONE))); + assertFalse(conditionApplies(map(ZERO, ONE), GT, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), GT, map(ONE, ZERO))); + assertFalse(conditionApplies(map(ONE, ZERO), GT, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE, TWO, ONE), GT, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), GT, map(ONE, ONE, TWO, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), GT, map())); + assertFalse(conditionApplies(map(), GT, map(ONE, ONE))); + + assertTrue(conditionApplies(map(ONE, ONE), GT, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertFalse(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), GT, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), GT, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertFalse(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), GT, map(ONE, ONE))); + assertFalse(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), GT, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertFalse(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), GT, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + + // GTE + assertTrue(conditionApplies(map(ONE, ONE), GTE, map(ONE, ONE))); + assertTrue(conditionApplies(map(), GTE, map())); + assertTrue(conditionApplies(map(ONE, ONE), GTE, map(ZERO, ONE))); + assertFalse(conditionApplies(map(ZERO, ONE), GTE, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), GTE, map(ONE, ZERO))); + assertFalse(conditionApplies(map(ONE, ZERO), GTE, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE, TWO, ONE), GTE, map(ONE, ONE))); + assertFalse(conditionApplies(map(ONE, ONE), GTE, map(ONE, ONE, TWO, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), GTE, map())); + assertFalse(conditionApplies(map(), GTE, map(ONE, ONE))); + + assertTrue(conditionApplies(map(ONE, ONE), GTE, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertFalse(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), GTE, map(ONE, ONE))); + assertTrue(conditionApplies(map(ONE, ONE), GTE, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + assertFalse(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), GTE, map(ONE, ONE))); + assertTrue(conditionApplies(map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE), GTE, map(ByteBufferUtil.EMPTY_BYTE_BUFFER, ONE))); + assertTrue(conditionApplies(map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER), GTE, map(ONE, ByteBufferUtil.EMPTY_BYTE_BUFFER))); + } +} diff --cc test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java index 54dd0c4c99,7424469b4c..f7848a3d03 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/CollectionsTest.java @@@ -21,14 -21,8 +21,15 @@@ import java.util.* import org.junit.Test; +import com.datastax.driver.core.ColumnDefinitions; +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Session; + import com.datastax.driver.core.utils.UUIDs; import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.cql3.ColumnSpecification; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.utils.FBUtilities; import static org.junit.Assert.assertEquals; @@@ -1916,67 -1073,76 +1917,165 @@@ public class CollectionsTest extends CQ "INSERT INTO %s (k, m) VALUES (0, {1 : (1, '1', 1.0, 1)})"); } + @Test + public void testSelectionOfEmptyCollections() throws Throwable + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, m frozen<map<text, int>>, s frozen<set<int>>)"); + + execute("INSERT INTO %s(k) VALUES (0)"); + execute("INSERT INTO %s(k, m, s) VALUES (1, {}, {})"); + execute("INSERT INTO %s(k, m, s) VALUES (2, ?, ?)", map(), set()); + execute("INSERT INTO %s(k, m, s) VALUES (3, {'2':2}, {2})"); + + beforeAndAfterFlush(() -> + { + assertRows(execute("SELECT m, s FROM %s WHERE k = 0"), row(null, null)); + assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 0"), row(null, null)); + assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 0"), row(null, null)); + assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 0"), row(null, null)); + + assertRows(execute("SELECT m, s FROM %s WHERE k = 1"), row(map(), set())); + assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 1"), row(null, null)); + assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 1"), row(map(), set())); + assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 1"), row(map(), set())); + + assertRows(execute("SELECT m, s FROM %s WHERE k = 2"), row(map(), set())); + assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 2"), row(null, null)); + assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 2"), row(map(), set())); + assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 2"), row(map(), set())); + + assertRows(execute("SELECT m, s FROM %s WHERE k = 3"), row(map("2", 2), set(2))); + assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 3"), row(null, null)); + assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 3"), row(map(), set())); + assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 3"), row(map(), set())); + }); + + createTable("CREATE TABLE %s (k int PRIMARY KEY, m map<text, int>, s set<int>)"); + + execute("INSERT INTO %s(k) VALUES (0)"); + execute("INSERT INTO %s(k, m, s) VALUES (1, {}, {})"); + execute("INSERT INTO %s(k, m, s) VALUES (2, ?, ?)", map(), set()); + execute("INSERT INTO %s(k, m, s) VALUES (3, {'2':2}, {2})"); + + beforeAndAfterFlush(() -> + { + assertRows(execute("SELECT m, s FROM %s WHERE k = 0"), row(null, null)); + assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 0"), row(null, null)); + assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 0"), row(null, null)); + assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 0"), row(null, null)); + + assertRows(execute("SELECT m, s FROM %s WHERE k = 1"), row(null, null)); + assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 1"), row(null, null)); + assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 1"), row(null, null)); + assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 1"), row(null, null)); + + assertRows(execute("SELECT m, s FROM %s WHERE k = 2"), row(null, null)); + assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 2"), row(null, null)); + assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 2"), row(null, null)); + assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 2"), row(null, null)); + + assertRows(execute("SELECT m, s FROM %s WHERE k = 3"), row(map("2", 2), set(2))); + assertRows(execute("SELECT m['0'], s[0] FROM %s WHERE k = 3"), row(null, null)); + assertRows(execute("SELECT m['0'..'1'], s[0..1] FROM %s WHERE k = 3"), row(null, null)); + assertRows(execute("SELECT m['0'..'1']['3'..'5'], s[0..1][3..5] FROM %s WHERE k = 3"), row(null, null)); + }); + } ++ + /* + 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 testInsertingMapDataWithParameterizedQueriesIsKeyOrderIndependentWithSelectSlice() 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));"); ++ beforeAndAfterFlush(() -> { ++ executeNet("INSERT INTO %s (k, c) VALUES ('0', ?)", linkedHashMap(uuid2, "2", uuid1, "1")); ++ // Make sure that we can slice either value out of the map ++ assertRowsNet(executeNet("SELECT k, c[" + uuid2 + "] from %s"), row("0", "2")); ++ assertRowsNet(executeNet("SELECT k, c[" + uuid1 + "] from %s"), row("0", "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 testInsertingSetDataWithParameterizedQueriesIsKeyOrderIndependentWithSelectSlice() throws Throwable ++ { ++ UUID uuid1 = UUIDs.timeBased(); ++ UUID uuid2 = UUIDs.timeBased(); ++ createTable("CREATE TABLE %s (k text, c frozen<set<timeuuid>>, PRIMARY KEY (k, c));"); ++ beforeAndAfterFlush(() -> { ++ executeNet("INSERT INTO %s (k, c) VALUES ('0', ?)", linkedHashSet(uuid2, uuid1)); ++ assertRowsNet(executeNet("SELECT k, c[" + uuid2 + "] from %s"), row("0", uuid2)); ++ assertRowsNet(executeNet("SELECT k, c[" + uuid1 + "] from %s"), row("0", uuid1)); ++ }); ++ } + + @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 --cc test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java index dd7085ab76,57910fbf6f..f43294c8ec --- a/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java +++ b/test/unit/org/apache/cassandra/io/sstable/CQLSSTableWriterTest.java @@@ -32,29 -31,29 +32,31 @@@ import com.google.common.collect.Immuta import com.google.common.collect.ImmutableMap; import com.google.common.io.Files; -import org.apache.commons.io.FileUtils; -import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; + import com.datastax.driver.core.utils.UUIDs; import org.apache.cassandra.SchemaLoader; import org.apache.cassandra.Util; import org.apache.cassandra.config.*; import org.apache.cassandra.cql3.*; import org.apache.cassandra.cql3.functions.UDHelper; +import org.apache.cassandra.cql3.functions.types.*; import org.apache.cassandra.db.Keyspace; +import org.apache.cassandra.db.commitlog.CommitLog; +import org.apache.cassandra.db.rows.Row; + import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.dht.*; import org.apache.cassandra.exceptions.*; +import org.apache.cassandra.schema.Schema; +import org.apache.cassandra.schema.TableMetadataRef; +import org.apache.cassandra.serializers.SimpleDateSerializer; import org.apache.cassandra.service.StorageService; +import org.apache.cassandra.transport.ProtocolVersion; 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; @@@ -120,7 -125,7 +123,7 @@@ public class CQLSSTableWriterTes loadSSTables(dataDir, keyspace); -- UntypedResultSet rs = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + ";"); ++ UntypedResultSet rs = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable); assertEquals(4, rs.size()); Iterator<UntypedResultSet.Row> iter = rs.iterator(); @@@ -639,44 -644,172 +642,210 @@@ assertEquals(100, resultSet.size()); } + @Test + public void testDateType() throws Exception + { + // Test to make sure we can write to `date` fields in both old and new formats + String schema = "CREATE TABLE " + qualifiedTable + " (" + + " k int," + + " c date," + + " PRIMARY KEY (k)" + + ")"; + String insert = "INSERT INTO " + qualifiedTable + " (k, c) VALUES (?, ?)"; + CQLSSTableWriter writer = CQLSSTableWriter.builder() + .inDirectory(dataDir) + .forTable(schema) + .using(insert) + .withBufferSizeInMB(1) + .build(); + + final int ID_OFFSET = 1000; + for (int i = 0; i < 100 ; i++) { + // Use old-style integer as date to test backwards-compatibility + writer.addRow(i, i - Integer.MIN_VALUE); // old-style raw integer needs to be offset + // Use new-style `LocalDate` for date value. + writer.addRow(i + ID_OFFSET, LocalDate.fromDaysSinceEpoch(i)); + } + writer.close(); + loadSSTables(dataDir, keyspace); + + UntypedResultSet rs = QueryProcessor.executeInternal("SELECT * FROM " + qualifiedTable + ";"); + assertEquals(200, rs.size()); + Map<Integer, LocalDate> map = StreamSupport.stream(rs.spliterator(), false) + .collect(Collectors.toMap( r -> r.getInt("k"), r -> r.getDate("c"))); + for (int i = 0; i < 100; i++) { + final LocalDate expected = LocalDate.fromDaysSinceEpoch(i); + assertEquals(expected, map.get(i + ID_OFFSET)); + assertEquals(expected, map.get(i)); + } + } + + @Test + public void testFrozenMapType() throws Exception + { + // 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, 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 + { + // 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, 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 + { + // 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, 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