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 <a.penya.gar...@gmail.com>
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: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to