This is an automated email from the ASF dual-hosted git repository.
adelapena pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new c95e5a5567 Improve vector value validation errors
c95e5a5567 is described below
commit c95e5a556709cf131ba074680585106559ff9511
Author: Andrés de la Peña <[email protected]>
AuthorDate: Thu Jul 6 13:01:55 2023 +0100
Improve vector value validation errors
patch by Andrés de la Peña; reviewed by Ekaterina Dimitrova and Maxwell Guo
for CASSANDRA-18652
---
CHANGES.txt | 1 +
.../apache/cassandra/db/marshal/VectorType.java | 15 +++-
.../cql3/validation/operations/CQLVectorTest.java | 90 ++++++++++++++++++++++
3 files changed, 103 insertions(+), 3 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index bcfdcfee58..e3c591378b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
5.0
+ * Improve vector value validation errors (CASSANDRA-18652)
* Upgrade Guava to 32.0.1 (CASSANDRA-18645)
* Add duration and count of partition keys to sstablemetadata
(CASSANDRA-18639)
* Remove deprecated compaction_tombstone_warning_threshold and
compaction_large_partition_warning_threshold from yaml (CASSANDRA-18626)
diff --git a/src/java/org/apache/cassandra/db/marshal/VectorType.java
b/src/java/org/apache/cassandra/db/marshal/VectorType.java
index a184be6bb6..631d990c6c 100644
--- a/src/java/org/apache/cassandra/db/marshal/VectorType.java
+++ b/src/java/org/apache/cassandra/db/marshal/VectorType.java
@@ -352,10 +352,11 @@ public final class VectorType<T> extends
AbstractType<List<T>>
}
}
- private static <V> void checkConsumedFully(V buffer, ValueAccessor<V>
accessor, int offset)
+ private <V> void checkConsumedFully(V buffer, ValueAccessor<V> accessor,
int offset)
{
- if (!accessor.isEmptyFromOffset(buffer, offset))
- throw new MarshalException("Unexpected extraneous bytes after
vector value");
+ int remaining = accessor.sizeFromOffset(buffer, offset);
+ if (remaining > 0)
+ throw new MarshalException("Unexpected " + remaining + "
extraneous bytes after " + asCQL3Type() + " value");
}
public abstract class VectorSerializer extends TypeSerializer<List<T>>
@@ -494,6 +495,11 @@ public final class VectorType<T> extends
AbstractType<List<T>>
return;
int offset = 0;
int elementSize = elementType.valueLengthIfFixed();
+
+ int expectedSize = elementSize * dimension;
+ if (accessor.size(input) < expectedSize)
+ throw new MarshalException("Not enough bytes to read a " +
asCQL3Type());
+
for (int i = 0; i < dimension; i++)
{
V bb = accessor.slice(input, offset, elementSize);
@@ -628,6 +634,9 @@ public final class VectorType<T> extends
AbstractType<List<T>>
int offset = 0;
for (int i = 0; i < dimension; i++)
{
+ if (offset >= accessor.size(input))
+ throw new MarshalException("Not enough bytes to read a " +
asCQL3Type());
+
V bb = readValue(input, accessor, offset);
offset += sizeOf(bb, accessor);
elementSerializer.validate(bb, accessor);
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 c1025659ca..ab39feb952 100644
---
a/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java
+++
b/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java
@@ -18,6 +18,7 @@
package org.apache.cassandra.cql3.validation.operations;
+import java.math.BigInteger;
import java.nio.ByteBuffer;
import org.junit.Assert;
@@ -139,6 +140,95 @@ public class CQLVectorTest extends CQLTester.InMemory
test.run();
}
+ @Test
+ public void invalidNumberOfDimensionsFixedWidth() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk int primary key, value vector<int,
2>)");
+
+ // fewer values than expected, with literals and bind markers
+ assertInvalidThrowMessage("Invalid vector literal for value of type
vector<int, 2>; expected 2 elements, but given 1",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0,
[1])");
+ assertInvalidThrowMessage("Not enough bytes to read a vector<int, 2>",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ?)",
vector(1));
+
+ // more values than expected, with literals and bind markers
+ assertInvalidThrowMessage("Invalid vector literal for value of type
vector<int, 2>; expected 2 elements, but given 3",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, [1,
2, 3])");
+ assertInvalidThrowMessage("Unexpected 4 extraneous bytes after
vector<int, 2> value",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ?)",
vector(1, 2, 3));
+ }
+
+ @Test
+ public void invalidNumberOfDimensionsVariableWidth() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk int primary key, value vector<text,
2>)");
+
+ // fewer values than expected, with literals and bind markers
+ assertInvalidThrowMessage("Invalid vector literal for value of type
vector<text, 2>; expected 2 elements, but given 1",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0,
['a'])");
+ assertInvalidThrowMessage("Not enough bytes to read a vector<text, 2>",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ?)",
vector("a"));
+
+ // more values than expected, with literals and bind markers
+ assertInvalidThrowMessage("Invalid vector literal for value of type
vector<text, 2>; expected 2 elements, but given 3",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ['a',
'b', 'c'])");
+ assertInvalidThrowMessage("Unexpected 2 extraneous bytes after
vector<text, 2> value",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ?)",
vector("a", "b", "c"));
+ }
+
+ @Test
+ public void invalidElementTypeFixedWidth() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk int primary key, value vector<int,
2>)");
+
+ // fixed-length bigint instead of int, with literals and bind markers
+ assertInvalidThrowMessage("Invalid vector literal for value: value
(bigint)1 is not of type int",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0,
[(bigint) 1, (bigint) 2])");
+ assertInvalidThrowMessage("Unexpected 8 extraneous bytes after
vector<int, 2> value",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ?)",
vector(1L, Long.MAX_VALUE));
+
+ // variable-length text instead of int, with literals and bind markers
+ assertInvalidThrowMessage("Invalid vector literal for value: value 'a'
is not of type int",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ['a',
'b'])");
+ assertInvalidThrowMessage("Not enough bytes to read a vector<int, 2>",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ?)",
vector("a", "b"));
+ }
+
+ @Test
+ public void invalidElementTypeVariableWidth() throws Throwable
+ {
+ createTable("CREATE TABLE %s (pk int primary key, value vector<text,
2>)");
+
+ // fixed-length int instead of text, with literals and bind markers
+ assertInvalidThrowMessage("Invalid vector literal for value: value 1
is not of type text",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, [1,
2])");
+ assertInvalidThrowMessage("Unexpected 6 extraneous bytes after
vector<text, 2> value",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ?)",
vector(1, 2));
+
+ // variable-length varint instead of text, with literals and bind
markers
+ assertInvalidThrowMessage("Invalid vector literal for value: value
(varint)1 is not of type text",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0,
[(varint) 1, (varint) 2])");
+ assertInvalidThrowMessage("String didn't validate.",
+ InvalidRequestException.class,
+ "INSERT INTO %s (pk, value) VALUES (0, ?)",
+
vector(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE), BigInteger.ONE));
+ }
+
@Test
public void update()
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]