diff --git a/CHANGES.txt b/CHANGES.txt index c80355c4f0..56128a4663 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,6 +5,7 @@ Merged from 4.1: * Add nodetool checktokenmetadata command that checks TokenMetadata is insync with Gossip endpointState (CASSANDRA-18758) * Equality check for Paxos.Electorate should not depend on collection types (CASSANDRA-19935) Merged from 4.0: + * Support UDTs and vectors as clustering keys in descending order (CASSANDRA-20050) * Fix CQL in snapshot's schema which did not contained UDTs used as reverse clustering columns (CASSANDRA-20036) * Add configurable batchlog endpoint strategies: random_remote, prefer_local, dynamic_remote, and dynamic (CASSANDRA-18120) * Fix bash-completion for debian distro (CASSANDRA-19999) diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java index 76276a70de..3c63934090 100644 --- a/src/java/org/apache/cassandra/cql3/UserTypes.java +++ b/src/java/org/apache/cassandra/cql3/UserTypes.java @@ -41,7 +41,7 @@ public abstract class UserTypes public static ColumnSpecification fieldSpecOf(ColumnSpecification column, int field) { - UserType ut = (UserType)column.type; + UserType ut = (UserType)column.type.unwrap(); return new ColumnSpecification(column.ksName, column.cfName, new ColumnIdentifier(column.name + "." + ut.fieldName(field), true), @@ -132,7 +132,7 @@ public abstract class UserTypes { validateAssignableTo(keyspace, receiver); - UserType ut = (UserType)receiver.type; + UserType ut = (UserType)receiver.type.unwrap(); boolean allTerminal = true; List values = new ArrayList<>(entries.size()); int foundValues = 0; @@ -161,16 +161,18 @@ public abstract class UserTypes } } - DelayedValue value = new DelayedValue(((UserType)receiver.type), values); + DelayedValue value = new DelayedValue(((UserType)receiver.type.unwrap()), values); return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; } private void validateAssignableTo(String keyspace, ColumnSpecification receiver) throws InvalidRequestException { - if (!receiver.type.isUDT()) + AbstractType unwrapped = receiver.type.unwrap(); + + if (!unwrapped.isUDT()) throw new InvalidRequestException(String.format("Invalid user type literal for %s of type %s", receiver.name, receiver.type.asCQL3Type())); - UserType ut = (UserType)receiver.type; + UserType ut = (UserType)unwrapped; for (int i = 0; i < ut.size(); i++) { FieldIdentifier field = ut.fieldName(i); diff --git a/src/java/org/apache/cassandra/cql3/Vectors.java b/src/java/org/apache/cassandra/cql3/Vectors.java index 2c53b9e716..152dd57936 100644 --- a/src/java/org/apache/cassandra/cql3/Vectors.java +++ b/src/java/org/apache/cassandra/cql3/Vectors.java @@ -115,9 +115,11 @@ public class Vectors @Override public Term prepare(String keyspace, ColumnSpecification receiver) throws InvalidRequestException { - if (!receiver.type.isVector()) + AbstractType unwrapped = receiver.type.unwrap(); + + if (!unwrapped.isVector()) throw new InvalidRequestException(String.format("Invalid vector literal for %s of type %s", receiver.name, receiver.type.asCQL3Type())); - VectorType type = (VectorType) receiver.type; + VectorType type = (VectorType) unwrapped; if (elements.size() != type.dimension) throw new InvalidRequestException(String.format("Invalid vector literal for %s of type %s; expected %d elements, but given %d", receiver.name, receiver.type.asCQL3Type(), type.dimension, elements.size())); diff --git a/src/java/org/apache/cassandra/cql3/selection/Selectable.java b/src/java/org/apache/cassandra/cql3/selection/Selectable.java index 56f7f85025..3319bb3b4e 100644 --- a/src/java/org/apache/cassandra/cql3/selection/Selectable.java +++ b/src/java/org/apache/cassandra/cql3/selection/Selectable.java @@ -526,7 +526,7 @@ public interface Selectable extends AssignmentTestable } Selector.Factory factory = selected.newSelectorFactory(table, expectedUdtType, defs, boundNames); - AbstractType type = factory.getReturnType(); + AbstractType type = factory.getReturnType().unwrap(); if (!type.isUDT()) { throw new InvalidRequestException( diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java index 7a1d73ea37..5a3b81f112 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UserTypesTest.java @@ -106,6 +106,22 @@ public class UserTypesTest extends CQLTester ); } + @Test + public void testDescendingOrderingOfUserTypesIsSupported() throws Throwable + { + String myType = createType("CREATE TYPE %s (x double)"); + createTable("CREATE TABLE %s (k int, v frozen<" + myType + ">, b boolean static, PRIMARY KEY (k, v)) WITH CLUSTERING ORDER BY (v DESC)"); + + execute("INSERT INTO %s(k, v) VALUES (?, {x:?})", 1, -104.99251); + execute("UPDATE %s SET b = ? WHERE k = ?", true, 1); + + beforeAndAfterFlush(() -> + assertRows(execute("SELECT v.x FROM %s WHERE k = ? AND v = {x:?}", 1, -104.99251), + row(-104.99251) + ) + ); + } + @Test public void testInvalidUDTStatements() throws Throwable { diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/VectorsTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/VectorsTest.java new file mode 100644 index 0000000000..7154d0295e --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/VectorsTest.java @@ -0,0 +1,46 @@ +/* + * 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.validation.entities; + +import java.util.List; + +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.cassandra.cql3.CQLTester; + +import static org.apache.cassandra.ServerTestUtils.daemonInitialization; + +public class VectorsTest extends CQLTester +{ + @BeforeClass + public static void setUpClass() // overrides CQLTester.setUpClass() + { + daemonInitialization(); + prepareServer(); + } + + @Test + public void testDescendingOrderingOfVectorIsSupported() throws Throwable + { + createTable("CREATE TABLE %s (k int, v vector, PRIMARY KEY (k, v)) WITH CLUSTERING ORDER BY (v DESC)"); + execute("INSERT INTO %s(k, v) VALUES (1, [1,2,3])"); + beforeAndAfterFlush(() -> assertRows(execute("SELECT v FROM %s WHERE k = 1 and v = [1,2,3]"), row(List.of(1, 2, 3)))); + } +}