This is an automated email from the ASF dual-hosted git repository. adelapena pushed a commit to branch cassandra-5.0 in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/cassandra-5.0 by this push: new 6befb178aa Remove support for empty values on the vector data type 6befb178aa is described below commit 6befb178aa0b7ad669f5c30e8b53fdd69a225a7a Author: Andrés de la Peña <a.penya.gar...@gmail.com> AuthorDate: Fri Sep 22 14:09:35 2023 +0100 Remove support for empty values on the vector data type patch by Andrés de la Peña; reviewed by David Capwell and Maxwell Guo for CASSANDRA-18876 --- CHANGES.txt | 1 + src/java/org/apache/cassandra/cql3/Constants.java | 2 +- .../db/marshal/AbstractCompositeType.java | 6 +++ .../cassandra/db/marshal/AbstractTimeUUIDType.java | 7 ++++ .../apache/cassandra/db/marshal/AbstractType.java | 2 +- .../apache/cassandra/db/marshal/BooleanType.java | 7 ++++ .../org/apache/cassandra/db/marshal/ByteType.java | 6 --- .../org/apache/cassandra/db/marshal/BytesType.java | 6 +++ .../cassandra/db/marshal/CollectionType.java | 6 --- .../cassandra/db/marshal/CounterColumnType.java | 7 ++++ .../apache/cassandra/db/marshal/DecimalType.java | 7 ++++ .../apache/cassandra/db/marshal/DoubleType.java | 7 ++++ .../apache/cassandra/db/marshal/DurationType.java | 6 --- .../org/apache/cassandra/db/marshal/FloatType.java | 7 ++++ .../cassandra/db/marshal/InetAddressType.java | 7 ++++ .../org/apache/cassandra/db/marshal/Int32Type.java | 7 ++++ .../apache/cassandra/db/marshal/IntegerType.java | 7 ++++ .../cassandra/db/marshal/LexicalUUIDType.java | 7 ++++ .../org/apache/cassandra/db/marshal/LongType.java | 7 ++++ .../org/apache/cassandra/db/marshal/ShortType.java | 6 --- .../cassandra/db/marshal/SimpleDateType.java | 6 --- .../apache/cassandra/db/marshal/StringType.java | 6 +++ .../org/apache/cassandra/db/marshal/TimeType.java | 6 --- .../apache/cassandra/db/marshal/TimestampType.java | 6 +++ .../org/apache/cassandra/db/marshal/TupleType.java | 6 +++ .../org/apache/cassandra/db/marshal/UUIDType.java | 7 ++++ .../apache/cassandra/db/marshal/VectorType.java | 44 +++++++++++++++------- .../cql3/validation/operations/CQLVectorTest.java | 43 +++++++++++++++++++++ 28 files changed, 190 insertions(+), 52 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bca22c4580..12c8fbc0e3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.0-alpha2 + * Remove support for empty values on the vector data type (CASSANDRA-18876) * Upgrade Dropwizard Metrics to 4.2.19 (CASSANDRA-14667) * Upgrade caffeine cache and fix CIDR permissions cache invalidation (CASSANDRA-18805) * Remove deprecated properties in CompressionParams (CASSANDRA-18742) diff --git a/src/java/org/apache/cassandra/cql3/Constants.java b/src/java/org/apache/cassandra/cql3/Constants.java index 03341d22bd..be81bed3b0 100644 --- a/src/java/org/apache/cassandra/cql3/Constants.java +++ b/src/java/org/apache/cassandra/cql3/Constants.java @@ -292,7 +292,7 @@ public abstract class Constants public AssignmentTestable.TestResult testAssignment(String keyspace, ColumnSpecification receiver) { CQL3Type receiverType = receiver.type.asCQL3Type(); - if (receiverType.isCollection() || receiverType.isUDT()) + if (receiverType.isCollection() || receiverType.isUDT() || receiverType.isVector()) return AssignmentTestable.TestResult.NOT_ASSIGNABLE; if (!(receiverType instanceof CQL3Type.Native)) diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java b/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java index 350c124fa1..170363594f 100644 --- a/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java +++ b/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java @@ -41,6 +41,12 @@ public abstract class AbstractCompositeType extends AbstractType<ByteBuffer> super(ComparisonType.CUSTOM); } + @Override + public boolean allowsEmpty() + { + return true; + } + public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR right, ValueAccessor<VR> accessorR) { if (accessorL.isEmpty(left) || accessorR.isEmpty(right)) diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java b/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java index 1c4088ee97..35778aff24 100644 --- a/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/AbstractTimeUUIDType.java @@ -42,6 +42,13 @@ public abstract class AbstractTimeUUIDType<T> extends TemporalType<T> super(ComparisonType.CUSTOM); } // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java index 3f4124011b..fe8b549837 100644 --- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java +++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java @@ -512,7 +512,7 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>, Assignm */ public boolean allowsEmpty() { - return true; + return false; } public boolean isNull(ByteBuffer bb) diff --git a/src/java/org/apache/cassandra/db/marshal/BooleanType.java b/src/java/org/apache/cassandra/db/marshal/BooleanType.java index acaba5ba7a..471aa8dc2d 100644 --- a/src/java/org/apache/cassandra/db/marshal/BooleanType.java +++ b/src/java/org/apache/cassandra/db/marshal/BooleanType.java @@ -38,6 +38,13 @@ public class BooleanType extends AbstractType<Boolean> BooleanType() {super(ComparisonType.CUSTOM);} // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/ByteType.java b/src/java/org/apache/cassandra/db/marshal/ByteType.java index 720168bcb3..614bdf9052 100644 --- a/src/java/org/apache/cassandra/db/marshal/ByteType.java +++ b/src/java/org/apache/cassandra/db/marshal/ByteType.java @@ -46,12 +46,6 @@ public class ByteType extends NumberType<Byte> super(ComparisonType.CUSTOM); } // singleton - @Override - public boolean allowsEmpty() - { - return false; - } - public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR right, ValueAccessor<VR> accessorR) { return accessorL.getByte(left, 0) - accessorR.getByte(right, 0); diff --git a/src/java/org/apache/cassandra/db/marshal/BytesType.java b/src/java/org/apache/cassandra/db/marshal/BytesType.java index b5c5cc8bdf..7ad0280e55 100644 --- a/src/java/org/apache/cassandra/db/marshal/BytesType.java +++ b/src/java/org/apache/cassandra/db/marshal/BytesType.java @@ -38,6 +38,12 @@ public class BytesType extends AbstractType<ByteBuffer> BytesType() {super(ComparisonType.BYTE_ORDER);} // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + public ByteBuffer fromString(String source) { try diff --git a/src/java/org/apache/cassandra/db/marshal/CollectionType.java b/src/java/org/apache/cassandra/db/marshal/CollectionType.java index 5e546cbc96..0f140fea06 100644 --- a/src/java/org/apache/cassandra/db/marshal/CollectionType.java +++ b/src/java/org/apache/cassandra/db/marshal/CollectionType.java @@ -93,12 +93,6 @@ public abstract class CollectionType<T> extends AbstractType<T> protected abstract List<ByteBuffer> serializedValues(Iterator<Cell<?>> cells); - @Override - public boolean allowsEmpty() - { - return false; - } - @Override public abstract CollectionSerializer<T> getSerializer(); diff --git a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java index b5ce7d829e..c0e1465bf1 100644 --- a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java +++ b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java @@ -37,6 +37,13 @@ public class CounterColumnType extends NumberType<Long> CounterColumnType() {super(ComparisonType.NOT_COMPARABLE);} // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/DecimalType.java b/src/java/org/apache/cassandra/db/marshal/DecimalType.java index 51d2561aa6..00da39ac35 100644 --- a/src/java/org/apache/cassandra/db/marshal/DecimalType.java +++ b/src/java/org/apache/cassandra/db/marshal/DecimalType.java @@ -65,6 +65,13 @@ public class DecimalType extends NumberType<BigDecimal> DecimalType() {super(ComparisonType.CUSTOM);} // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/DoubleType.java b/src/java/org/apache/cassandra/db/marshal/DoubleType.java index e1062dc538..eff3bf744d 100644 --- a/src/java/org/apache/cassandra/db/marshal/DoubleType.java +++ b/src/java/org/apache/cassandra/db/marshal/DoubleType.java @@ -42,6 +42,13 @@ public class DoubleType extends NumberType<Double> DoubleType() {super(ComparisonType.CUSTOM);} // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/DurationType.java b/src/java/org/apache/cassandra/db/marshal/DurationType.java index 9353b2f6b7..0c46617554 100644 --- a/src/java/org/apache/cassandra/db/marshal/DurationType.java +++ b/src/java/org/apache/cassandra/db/marshal/DurationType.java @@ -46,12 +46,6 @@ public class DurationType extends AbstractType<Duration> super(ComparisonType.BYTE_ORDER); } // singleton - @Override - public boolean allowsEmpty() - { - return false; - } - public ByteBuffer fromString(String source) throws MarshalException { // Return an empty ByteBuffer for an empty string. diff --git a/src/java/org/apache/cassandra/db/marshal/FloatType.java b/src/java/org/apache/cassandra/db/marshal/FloatType.java index abed92960d..216b077811 100644 --- a/src/java/org/apache/cassandra/db/marshal/FloatType.java +++ b/src/java/org/apache/cassandra/db/marshal/FloatType.java @@ -43,6 +43,13 @@ public class FloatType extends NumberType<Float> FloatType() {super(ComparisonType.CUSTOM);} // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java index 820e0c2f77..64e178d989 100644 --- a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java +++ b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java @@ -41,6 +41,13 @@ public class InetAddressType extends AbstractType<InetAddress> InetAddressType() {super(ComparisonType.BYTE_ORDER);} // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/Int32Type.java b/src/java/org/apache/cassandra/db/marshal/Int32Type.java index 68c32ad90e..c76d310764 100644 --- a/src/java/org/apache/cassandra/db/marshal/Int32Type.java +++ b/src/java/org/apache/cassandra/db/marshal/Int32Type.java @@ -46,6 +46,13 @@ public class Int32Type extends NumberType<Integer> super(ComparisonType.CUSTOM); } // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/IntegerType.java b/src/java/org/apache/cassandra/db/marshal/IntegerType.java index 99fac1640d..2c4b791d83 100644 --- a/src/java/org/apache/cassandra/db/marshal/IntegerType.java +++ b/src/java/org/apache/cassandra/db/marshal/IntegerType.java @@ -80,6 +80,13 @@ public final class IntegerType extends NumberType<BigInteger> IntegerType() {super(ComparisonType.CUSTOM);}/* singleton */ + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java index 498ca49584..e37f8e2f21 100644 --- a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java @@ -47,6 +47,13 @@ public class LexicalUUIDType extends AbstractType<UUID> super(ComparisonType.CUSTOM); } // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/LongType.java b/src/java/org/apache/cassandra/db/marshal/LongType.java index e0348fdb11..97b9f75468 100644 --- a/src/java/org/apache/cassandra/db/marshal/LongType.java +++ b/src/java/org/apache/cassandra/db/marshal/LongType.java @@ -43,6 +43,13 @@ public class LongType extends NumberType<Long> LongType() {super(ComparisonType.CUSTOM);} // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/ShortType.java b/src/java/org/apache/cassandra/db/marshal/ShortType.java index bd7c2fd653..96047daf73 100644 --- a/src/java/org/apache/cassandra/db/marshal/ShortType.java +++ b/src/java/org/apache/cassandra/db/marshal/ShortType.java @@ -46,12 +46,6 @@ public class ShortType extends NumberType<Short> super(ComparisonType.CUSTOM); } // singleton - @Override - public boolean allowsEmpty() - { - return false; - } - public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR right, ValueAccessor<VR> accessorR) { int diff = accessorL.getByte(left, 0) - accessorR.getByte(right, 0); diff --git a/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java b/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java index 54eabafc0c..a474d39a81 100644 --- a/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java +++ b/src/java/org/apache/cassandra/db/marshal/SimpleDateType.java @@ -43,12 +43,6 @@ public class SimpleDateType extends TemporalType<Integer> SimpleDateType() {super(ComparisonType.BYTE_ORDER);} // singleton - @Override - public boolean allowsEmpty() - { - return false; - } - @Override public <V> ByteSource asComparableBytes(ValueAccessor<V> accessor, V data, Version version) { diff --git a/src/java/org/apache/cassandra/db/marshal/StringType.java b/src/java/org/apache/cassandra/db/marshal/StringType.java index d823632b73..dd21bedc49 100644 --- a/src/java/org/apache/cassandra/db/marshal/StringType.java +++ b/src/java/org/apache/cassandra/db/marshal/StringType.java @@ -27,6 +27,12 @@ public abstract class StringType extends AbstractType<String> super(comparisonType); } + @Override + public boolean allowsEmpty() + { + return true; + } + public ByteBuffer concat(String left, String right) { return decompose(left + right); diff --git a/src/java/org/apache/cassandra/db/marshal/TimeType.java b/src/java/org/apache/cassandra/db/marshal/TimeType.java index 2a87808f1b..67cf7dbceb 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimeType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimeType.java @@ -47,12 +47,6 @@ public class TimeType extends TemporalType<Long> private TimeType() {super(ComparisonType.BYTE_ORDER);} // singleton - @Override - public boolean allowsEmpty() - { - return false; - } - public ByteBuffer fromString(String source) throws MarshalException { return decompose(TimeSerializer.timeStringToLong(source)); diff --git a/src/java/org/apache/cassandra/db/marshal/TimestampType.java b/src/java/org/apache/cassandra/db/marshal/TimestampType.java index 67976769bd..54d88bd0da 100644 --- a/src/java/org/apache/cassandra/db/marshal/TimestampType.java +++ b/src/java/org/apache/cassandra/db/marshal/TimestampType.java @@ -55,6 +55,12 @@ public class TimestampType extends TemporalType<Date> private TimestampType() {super(ComparisonType.CUSTOM);} // singleton + @Override + public boolean allowsEmpty() + { + return true; + } + public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/TupleType.java b/src/java/org/apache/cassandra/db/marshal/TupleType.java index 79f921920d..0a03936da7 100644 --- a/src/java/org/apache/cassandra/db/marshal/TupleType.java +++ b/src/java/org/apache/cassandra/db/marshal/TupleType.java @@ -76,6 +76,12 @@ public class TupleType extends AbstractType<ByteBuffer> this.serializer = new TupleSerializer(fieldSerializers(types)); } + @Override + public boolean allowsEmpty() + { + return true; + } + private static List<TypeSerializer<?>> fieldSerializers(List<AbstractType<?>> types) { int size = types.size(); diff --git a/src/java/org/apache/cassandra/db/marshal/UUIDType.java b/src/java/org/apache/cassandra/db/marshal/UUIDType.java index c949524613..a5abd92234 100644 --- a/src/java/org/apache/cassandra/db/marshal/UUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/UUIDType.java @@ -59,6 +59,13 @@ public class UUIDType extends AbstractType<UUID> super(ComparisonType.CUSTOM); } + @Override + public boolean allowsEmpty() + { + return true; + } + + @Override public boolean isEmptyValueMeaningless() { return true; diff --git a/src/java/org/apache/cassandra/db/marshal/VectorType.java b/src/java/org/apache/cassandra/db/marshal/VectorType.java index 7caf02e3bb..204c603d28 100644 --- a/src/java/org/apache/cassandra/db/marshal/VectorType.java +++ b/src/java/org/apache/cassandra/db/marshal/VectorType.java @@ -25,6 +25,8 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nullable; + import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.cql3.Term; import org.apache.cassandra.cql3.Vectors; @@ -172,10 +174,10 @@ public final class VectorType<T> extends AbstractType<List<T>> public <V> V decomposeAsFloat(ValueAccessor<V> accessor, float[] value) { + if (value == null) + rejectNullOrEmptyValue(); if (!(elementType instanceof FloatType)) throw new IllegalStateException("Attempted to read as float, but element type is " + elementType.asCQL3Type()); - if (value == null) - return null; if (value.length != dimension) throw new IllegalArgumentException(String.format("Attempted to add float vector of dimension %d to %s", value.length, asCQL3Type())); // TODO : should we use TypeSizes to be consistent with other code? Its the same value at the end of the day... @@ -215,7 +217,8 @@ public final class VectorType<T> extends AbstractType<List<T>> public <V> V fromComparableBytes(ValueAccessor<V> accessor, ByteSource.Peekable comparableBytes, ByteComparable.Version version) { if (comparableBytes == null) - return accessor.empty(); + rejectNullOrEmptyValue(); + assert version != ByteComparable.Version.LEGACY; // legacy translation is not reversible List<V> buffers = new ArrayList<>(); @@ -358,6 +361,11 @@ public final class VectorType<T> extends AbstractType<List<T>> if (remaining > 0) throw new MarshalException("Unexpected " + remaining + " extraneous bytes after " + asCQL3Type() + " value"); } + + private static void rejectNullOrEmptyValue() + { + throw new MarshalException("Invalid empty vector value"); + } public abstract class VectorSerializer extends TypeSerializer<List<T>> { @@ -390,6 +398,13 @@ public final class VectorType<T> extends AbstractType<List<T>> { return (Class) List.class; } + + @Override + public <V> boolean isNull(@Nullable V buffer, ValueAccessor<V> accessor) + { + // we don't allow empty vectors, so we can just check for null + return buffer == null; + } } private class FixedLengthSerializer extends VectorSerializer @@ -404,8 +419,6 @@ public final class VectorType<T> extends AbstractType<List<T>> { if (elementType.isByteOrderComparable) return ValueAccessor.compare(left, accessorL, right, accessorR); - if (accessorL.isEmpty(left) || accessorR.isEmpty(right)) - return Boolean.compare(accessorR.isEmpty(right), accessorL.isEmpty(left)); int offset = 0; int elementLength = elementType.valueLengthIfFixed(); for (int i = 0; i < dimension; i++) @@ -443,7 +456,8 @@ public final class VectorType<T> extends AbstractType<List<T>> public <V> V serializeRaw(List<V> value, ValueAccessor<V> accessor) { if (value == null) - return accessor.empty(); + rejectNullOrEmptyValue(); + check(value); int size = elementType.valueLengthIfFixed(); @@ -458,7 +472,8 @@ public final class VectorType<T> extends AbstractType<List<T>> public ByteBuffer serialize(List<T> value) { if (value == null) - return ByteBufferUtil.EMPTY_BYTE_BUFFER; + rejectNullOrEmptyValue(); + check(value); ByteBuffer bb = ByteBuffer.allocate(elementType.valueLengthIfFixed() * dimension); @@ -492,7 +507,8 @@ public final class VectorType<T> extends AbstractType<List<T>> public <V> void validate(V input, ValueAccessor<V> accessor) throws MarshalException { if (accessor.isEmpty(input)) - return; + rejectNullOrEmptyValue(); + int offset = 0; int elementSize = elementType.valueLengthIfFixed(); @@ -520,9 +536,6 @@ public final class VectorType<T> extends AbstractType<List<T>> public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR right, ValueAccessor<VR> accessorR) { - if (accessorL.isEmpty(left) || accessorR.isEmpty(right)) - return Boolean.compare(accessorR.isEmpty(right), accessorL.isEmpty(left)); - int leftOffset = 0; int rightOffset = 0; for (int i = 0; i < dimension; i++) @@ -584,7 +597,8 @@ public final class VectorType<T> extends AbstractType<List<T>> public <V> V serializeRaw(List<V> value, ValueAccessor<V> accessor) { if (value == null) - return accessor.empty(); + rejectNullOrEmptyValue(); + check(value); V bb = accessor.allocate(value.stream().mapToInt(v -> sizeOf(v, accessor)).sum()); @@ -598,7 +612,8 @@ public final class VectorType<T> extends AbstractType<List<T>> public ByteBuffer serialize(List<T> value) { if (value == null) - return ByteBufferUtil.EMPTY_BYTE_BUFFER; + rejectNullOrEmptyValue(); + check(value); List<ByteBuffer> bbs = new ArrayList<>(dimension); @@ -630,7 +645,8 @@ public final class VectorType<T> extends AbstractType<List<T>> public <V> void validate(V input, ValueAccessor<V> accessor) throws MarshalException { if (accessor.isEmpty(input)) - return; + rejectNullOrEmptyValue(); + int offset = 0; for (int i = 0; i < dimension; i++) { diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java index cf88d32d93..42daab6ada 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.validation.operations; import java.math.BigInteger; import java.nio.ByteBuffer; +import java.util.List; import org.junit.Assert; import org.junit.Test; @@ -34,6 +35,7 @@ import org.apache.cassandra.db.marshal.Int32Type; import org.apache.cassandra.db.marshal.VectorType; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.transport.ProtocolVersion; +import org.apache.cassandra.utils.ByteBufferUtil; import org.assertj.core.api.Assertions; import static java.lang.String.format; @@ -261,6 +263,47 @@ public class CQLVectorTest extends CQLTester.InMemory test.run(); } + @Test + public void nullValues() + { + assertAcceptsNullValues("int"); // fixed length + assertAcceptsNullValues("float"); // fixed length with special/optimized treatment + assertAcceptsNullValues("text"); // variable length + } + + private void assertAcceptsNullValues(String type) + { + createTable(format("CREATE TABLE %%s (k int primary key, v vector<%s, 2>)", type)); + + execute("INSERT INTO %s (k, v) VALUES (0, null)"); + assertRows(execute("SELECT * FROM %s"), row(0, null)); + + execute("INSERT INTO %s (k, v) VALUES (0, ?)", (List<Integer>) null); + assertRows(execute("SELECT * FROM %s"), row(0, null)); + } + + @Test + public void emptyValues() throws Throwable + { + assertRejectsEmptyValues("int"); // fixed length + assertRejectsEmptyValues("float"); // fixed length with special/optimized treatment + assertRejectsEmptyValues("text"); // variable length + } + + private void assertRejectsEmptyValues(String type) throws Throwable + { + createTable(format("CREATE TABLE %%s (k int primary key, v vector<%s, 2>)", type)); + + assertInvalidThrowMessage(format("Invalid HEX constant (0x) for \"v\" of type vector<%s, 2>", type), + InvalidRequestException.class, + "INSERT INTO %s (k, v) VALUES (0, 0x)"); + + assertInvalidThrowMessage("Invalid empty vector value", + InvalidRequestException.class, + "INSERT INTO %s (k, v) VALUES (0, ?)", + ByteBufferUtil.EMPTY_BYTE_BUFFER); + } + @Test public void functions() { --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org