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 ddbc52990f Add support for vectors in UDFs
ddbc52990f is described below

commit ddbc52990f90473db729e96f22d2914e51a957a6
Author: Andrés de la Peña <[email protected]>
AuthorDate: Thu Jun 22 19:33:25 2023 +0100

    Add support for vectors in UDFs
    
    patch by Andrés de la Peña; reviewed by Berenguer Blasi and Maxwell Guo for 
CASSANDRA-18613
---
 CHANGES.txt                                        |   1 +
 .../types/AbstractAddressableByIndexData.java      |   6 +
 .../cql3/functions/types/AbstractData.java         |  16 ++
 .../types/AbstractGettableByIndexData.java         |  22 +++
 .../cql3/functions/types/AbstractGettableData.java |  18 ++
 .../cql3/functions/types/CodecRegistry.java        |  69 ++++---
 .../cassandra/cql3/functions/types/DataType.java   |  15 +-
 .../functions/types/DataTypeClassNameParser.java   |  14 ++
 .../cql3/functions/types/GettableByIndexData.java  |  44 +++++
 .../cql3/functions/types/GettableByNameData.java   |  44 +++++
 .../cql3/functions/types/SettableByIndexData.java  |  14 ++
 .../cql3/functions/types/SettableByNameData.java   |  14 ++
 .../cassandra/cql3/functions/types/TypeCodec.java  |  80 ++++++++
 .../cassandra/cql3/functions/types/TypeTokens.java |  19 ++
 .../cql3/functions/types/VectorCodec.java          | 204 +++++++++++++++++++++
 .../cassandra/cql3/functions/types/VectorType.java |  88 +++++++++
 .../cassandra/cql3/selection/Selectable.java       |   3 +-
 .../apache/cassandra/utils/JavaDriverUtils.java    |  12 --
 .../cql3/validation/entities/UFSecurityTest.java   |   8 +-
 .../cql3/validation/operations/CQLVectorTest.java  | 173 +++++++++++++++--
 20 files changed, 807 insertions(+), 57 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index d79d78bad2..caea458186 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 5.0
+ * Add support for vectors in UDFs (CASSANDRA-18613)
  * 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)
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/AbstractAddressableByIndexData.java
 
b/src/java/org/apache/cassandra/cql3/functions/types/AbstractAddressableByIndexData.java
index 0e98c8948f..2b1fd84a9e 100644
--- 
a/src/java/org/apache/cassandra/cql3/functions/types/AbstractAddressableByIndexData.java
+++ 
b/src/java/org/apache/cassandra/cql3/functions/types/AbstractAddressableByIndexData.java
@@ -253,6 +253,12 @@ extends AbstractGettableByIndexData implements 
SettableByIndexData<T>
         return setValue(i, codecFor(i, 
TypeTokens.setOf(elementsType)).serialize(v, protocolVersion));
     }
 
+    @Override
+    public <E> T setVector(int i, List<E> v)
+    {
+        return setValue(i, codecFor(i).serialize(v, protocolVersion));
+    }
+
     @Override
     public T setUDTValue(int i, UDTValue v)
     {
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/AbstractData.java 
b/src/java/org/apache/cassandra/cql3/functions/types/AbstractData.java
index adb7c0e0bd..45ce309332 100644
--- a/src/java/org/apache/cassandra/cql3/functions/types/AbstractData.java
+++ b/src/java/org/apache/cassandra/cql3/functions/types/AbstractData.java
@@ -540,6 +540,22 @@ implements SettableData<T>
         return wrapped;
     }
 
+    @Override
+    public <E> T setVector(int i, List<E> v)
+    {
+        return setValue(i, codecFor(i).serialize(v, protocolVersion));
+    }
+
+    @Override
+    public <E> T setVector(String name, List<E> v)
+    {
+        for (int i : getAllIndexesOf(name))
+        {
+            setVector(i, v);
+        }
+        return wrapped;
+    }
+
     @Override
     public T setUDTValue(int i, UDTValue v)
     {
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/AbstractGettableByIndexData.java
 
b/src/java/org/apache/cassandra/cql3/functions/types/AbstractGettableByIndexData.java
index 1552309edb..ca8827b0d7 100644
--- 
a/src/java/org/apache/cassandra/cql3/functions/types/AbstractGettableByIndexData.java
+++ 
b/src/java/org/apache/cassandra/cql3/functions/types/AbstractGettableByIndexData.java
@@ -365,6 +365,28 @@ abstract class AbstractGettableByIndexData implements 
GettableByIndexData
         return codecFor(i, javaType).deserialize(value, protocolVersion);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> List<T> getVector(int i, Class<T> elementsClass)
+    {
+        return getList(i, TypeToken.of(elementsClass));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> List<T> getVector(int i, TypeToken<T> elementsType)
+    {
+        ByteBuffer value = getValue(i);
+        TypeToken<List<T>> javaType = TypeTokens.listOf(elementsType);
+        return codecFor(i, javaType).deserialize(value, protocolVersion);
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/AbstractGettableData.java 
b/src/java/org/apache/cassandra/cql3/functions/types/AbstractGettableData.java
index 2ac7d980f4..f26abd296c 100644
--- 
a/src/java/org/apache/cassandra/cql3/functions/types/AbstractGettableData.java
+++ 
b/src/java/org/apache/cassandra/cql3/functions/types/AbstractGettableData.java
@@ -269,6 +269,24 @@ implements GettableData
         return getMap(getIndexOf(name), keysType, valuesType);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> List<T> getVector(String name, Class<T> elementsClass)
+    {
+        return getList(getIndexOf(name), elementsClass);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> List<T> getVector(String name, TypeToken<T> elementsType)
+    {
+        return getList(getIndexOf(name), elementsType);
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/CodecRegistry.java 
b/src/java/org/apache/cassandra/cql3/functions/types/CodecRegistry.java
index a979a57269..706ef41a41 100644
--- a/src/java/org/apache/cassandra/cql3/functions/types/CodecRegistry.java
+++ b/src/java/org/apache/cassandra/cql3/functions/types/CodecRegistry.java
@@ -165,27 +165,27 @@ public final class CodecRegistry
 
     static
     {
-        BUILT_IN_CODECS_MAP.put(DataType.Name.ASCII, TypeCodec.ascii());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.BIGINT, TypeCodec.bigint());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.BLOB, TypeCodec.blob());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.BOOLEAN, TypeCodec.cboolean());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.COUNTER, TypeCodec.counter());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.DECIMAL, TypeCodec.decimal());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.DOUBLE, TypeCodec.cdouble());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.FLOAT, TypeCodec.cfloat());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.INET, TypeCodec.inet());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.INT, TypeCodec.cint());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.TEXT, TypeCodec.varchar());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.TIMESTAMP, 
TypeCodec.timestamp());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.UUID, TypeCodec.uuid());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.VARCHAR, TypeCodec.varchar());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.VARINT, TypeCodec.varint());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.TIMEUUID, TypeCodec.timeUUID());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.SMALLINT, TypeCodec.smallInt());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.TINYINT, TypeCodec.tinyInt());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.DATE, TypeCodec.date());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.TIME, TypeCodec.time());
-        BUILT_IN_CODECS_MAP.put(DataType.Name.DURATION, TypeCodec.duration());
+        BUILT_IN_CODECS_MAP.put(ASCII, TypeCodec.ascii());
+        BUILT_IN_CODECS_MAP.put(BIGINT, TypeCodec.bigint());
+        BUILT_IN_CODECS_MAP.put(BLOB, TypeCodec.blob());
+        BUILT_IN_CODECS_MAP.put(BOOLEAN, TypeCodec.cboolean());
+        BUILT_IN_CODECS_MAP.put(COUNTER, TypeCodec.counter());
+        BUILT_IN_CODECS_MAP.put(DECIMAL, TypeCodec.decimal());
+        BUILT_IN_CODECS_MAP.put(DOUBLE, TypeCodec.cdouble());
+        BUILT_IN_CODECS_MAP.put(FLOAT, TypeCodec.cfloat());
+        BUILT_IN_CODECS_MAP.put(INET, TypeCodec.inet());
+        BUILT_IN_CODECS_MAP.put(INT, TypeCodec.cint());
+        BUILT_IN_CODECS_MAP.put(TEXT, TypeCodec.varchar());
+        BUILT_IN_CODECS_MAP.put(TIMESTAMP, TypeCodec.timestamp());
+        BUILT_IN_CODECS_MAP.put(UUID, TypeCodec.uuid());
+        BUILT_IN_CODECS_MAP.put(VARCHAR, TypeCodec.varchar());
+        BUILT_IN_CODECS_MAP.put(VARINT, TypeCodec.varint());
+        BUILT_IN_CODECS_MAP.put(TIMEUUID, TypeCodec.timeUUID());
+        BUILT_IN_CODECS_MAP.put(SMALLINT, TypeCodec.smallInt());
+        BUILT_IN_CODECS_MAP.put(TINYINT, TypeCodec.tinyInt());
+        BUILT_IN_CODECS_MAP.put(DATE, TypeCodec.date());
+        BUILT_IN_CODECS_MAP.put(TIME, TypeCodec.time());
+        BUILT_IN_CODECS_MAP.put(DURATION, TypeCodec.duration());
     }
 
     // roughly sorted by popularity
@@ -318,6 +318,16 @@ public final class CodecRegistry
                     }
                     return weight;
                 }
+                case VECTOR:
+                {
+                    int weight = level;
+                    DataType eltType = cqlType.getTypeArguments().get(0);
+                    if (eltType != null)
+                    {
+                        weight += weigh(eltType, level + 1);
+                    }
+                    return weight == 0 ? 1 : weight;
+                }
                 case UDT:
                 {
                     int weight = level;
@@ -746,6 +756,13 @@ public final class CodecRegistry
             return (TypeCodec<T>) TypeCodec.map(keyCodec, valueCodec);
         }
 
+        if (cqlType instanceof VectorType
+            && (javaType == null || 
List.class.isAssignableFrom(javaType.getRawType())))
+        {
+            VectorType type = (VectorType) cqlType;
+            return (TypeCodec<T>) TypeCodec.vector(type, 
findCodec(type.getSubtype(), null));
+        }
+
         if (cqlType instanceof TupleType
             && (javaType == null || 
TupleValue.class.isAssignableFrom(javaType.getRawType())))
         {
@@ -847,14 +864,20 @@ public final class CodecRegistry
             }
         }
 
-        if ((cqlType == null || cqlType.getName() == DataType.Name.TUPLE)
+        if ((cqlType == null || cqlType.getName() == VECTOR) && value 
instanceof List)
+        {
+            VectorType type = (VectorType) cqlType;
+            return (TypeCodec<T>) TypeCodec.vector(type, 
findCodec(type.getSubtype(), null));
+        }
+
+        if ((cqlType == null || cqlType.getName() == TUPLE)
             && value instanceof TupleValue)
         {
             return (TypeCodec<T>)
                    TypeCodec.tuple(cqlType == null ? ((TupleValue) 
value).getType() : (TupleType) cqlType);
         }
 
-        if ((cqlType == null || cqlType.getName() == DataType.Name.UDT) && 
value instanceof UDTValue)
+        if ((cqlType == null || cqlType.getName() == UDT) && value instanceof 
UDTValue)
         {
             return (TypeCodec<T>)
                    TypeCodec.userType(cqlType == null ? ((UDTValue) 
value).getType() : (UserType) cqlType);
diff --git a/src/java/org/apache/cassandra/cql3/functions/types/DataType.java 
b/src/java/org/apache/cassandra/cql3/functions/types/DataType.java
index 54127202b1..fafc721b99 100644
--- a/src/java/org/apache/cassandra/cql3/functions/types/DataType.java
+++ b/src/java/org/apache/cassandra/cql3/functions/types/DataType.java
@@ -74,7 +74,8 @@ public abstract class DataType
         MAP(33),
         SET(34),
         UDT(48, ProtocolVersion.V3),
-        TUPLE(49, ProtocolVersion.V3);
+        TUPLE(49, ProtocolVersion.V3),
+        VECTOR(50, ProtocolVersion.V5);
 
         final int protocolId;
 
@@ -437,6 +438,18 @@ public abstract class DataType
         return map(keyType, valueType, false);
     }
 
+    /**
+     * Returns the type of vector of {@code elementType} elements with {@code 
dimensions} dimensions.
+     *
+     * @param elementType the type of the vector elements.
+     * @param dimensions  the number of dimensions of the vector.
+     * @return the type of vectors of {@code elementType} elements and {@code 
dimensions} dimensions.
+     */
+    public static VectorType vector(DataType elementType, int dimensions)
+    {
+        return new VectorType(elementType, dimensions);
+    }
+
     /**
      * Returns a Custom type.
      *
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/DataTypeClassNameParser.java
 
b/src/java/org/apache/cassandra/cql3/functions/types/DataTypeClassNameParser.java
index 7064ba22c7..1d50edfd86 100644
--- 
a/src/java/org/apache/cassandra/cql3/functions/types/DataTypeClassNameParser.java
+++ 
b/src/java/org/apache/cassandra/cql3/functions/types/DataTypeClassNameParser.java
@@ -53,6 +53,7 @@ public class DataTypeClassNameParser
     private static final String UDT_TYPE = 
"org.apache.cassandra.db.marshal.UserType";
     private static final String TUPLE_TYPE = 
"org.apache.cassandra.db.marshal.TupleType";
     private static final String DURATION_TYPE = 
"org.apache.cassandra.db.marshal.DurationType";
+    private static final String VECTOR_TYPE = 
"org.apache.cassandra.db.marshal.VectorType";
 
     private static final ImmutableMap<String, DataType> cassTypeToDataType =
     new ImmutableMap.Builder<String, DataType>()
@@ -118,6 +119,14 @@ public class DataTypeClassNameParser
             "Got o.a.c.db.marshal.FrozenType for something else than a 
collection, "
             + "this driver version might be too old for your version of 
Cassandra");
 
+        if (isVectorType(next))
+        {
+            List<String> parameters = parser.getTypeParameters();
+            DataType subtype = parseOne(parameters.get(0), protocolVersion, 
codecRegistry);
+            int dimensions = Integer.parseInt(parameters.get(1));
+            return DataType.vector(subtype, dimensions);
+        }
+
         if (isUserType(next))
         {
             ++parser.idx; // skipping '('
@@ -173,6 +182,11 @@ public class DataTypeClassNameParser
         return className;
     }
 
+    private static boolean isVectorType(String className)
+    {
+        return className.startsWith(VECTOR_TYPE);
+    }
+
     private static boolean isUserType(String className)
     {
         return className.startsWith(UDT_TYPE);
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/GettableByIndexData.java 
b/src/java/org/apache/cassandra/cql3/functions/types/GettableByIndexData.java
index bcc5585df4..abbe32b29b 100644
--- 
a/src/java/org/apache/cassandra/cql3/functions/types/GettableByIndexData.java
+++ 
b/src/java/org/apache/cassandra/cql3/functions/types/GettableByIndexData.java
@@ -465,6 +465,50 @@ public interface GettableByIndexData
      */
     public <K, V> Map<K, V> getMap(int i, TypeToken<K> keysType, TypeToken<V> 
valuesType);
 
+    /**
+     * Returns the {@code i}th value as a vector, represented as a Java list.
+     *
+     * <p>This method uses the {@link CodecRegistry} to find a codec to 
convert the underlying CQL
+     * type to a vector of the specified type.
+     *
+     * <p>If the type of the elements is generic, use {@link #getVector(int, 
TypeToken)}.
+     *
+     * <p>Implementation note: the actual {@link List} implementation 
representing the vector will depend on the {@link
+     * TypeCodec codec} being used; therefore, callers should make no 
assumptions concerning its mutability nor its
+     * thread-safety.
+     *
+     * @param i             the index ({@code 0 <= i < size()}) to retrieve.
+     * @param elementsClass the class for the elements of the list to retrieve.
+     * @return the value of the {@code i}th element as a list of {@code T} 
objects.
+     * @throws IndexOutOfBoundsException if {@code i} is not a valid index for 
this object.
+     * @throws CodecNotFoundException    if there is no registered codec to 
convert the element's CQL type to a vector.
+     */
+    public <T> List<T> getVector(int i, Class<T> elementsClass);
+
+    /**
+     * Returns the {@code i}th value as a vector, represented as a Java list.
+     *
+     * <p>This method uses the {@link CodecRegistry} to find a codec to 
convert the underlying CQL
+     * type to a vector of the specified type.
+     *
+     * <p>Use this variant with nested types, which produce a generic element 
type:
+     *
+     * <pre>
+     * {@code List<List<String>> l = row.getVector(1, new 
TypeToken<List<String>>() {});}
+     * </pre>
+     *
+     * <p>Implementation note: the actual {@link List} implementation 
representing the vector will depend on the {@link
+     * TypeCodec codec} being used; therefore, callers should make no 
assumptions concerning its mutability nor its
+     * thread-safety.
+     *
+     * @param i            the index ({@code 0 <= i < size()}) to retrieve.
+     * @param elementsType the type of the elements of the vector to retrieve.
+     * @return the value of the {@code i}th element as a vector of {@code T} 
objects.
+     * @throws IndexOutOfBoundsException if {@code i} is not a valid index for 
this object.
+     * @throws CodecNotFoundException    if there is no registered codec to 
convert the element's CQL type to a vector.
+     */
+    public <T> List<T> getVector(int i, TypeToken<T> elementsType);
+
     /**
      * Return the {@code i}th value as a UDT value.
      *
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/GettableByNameData.java 
b/src/java/org/apache/cassandra/cql3/functions/types/GettableByNameData.java
index c578ff4cff..d65bf36d19 100644
--- a/src/java/org/apache/cassandra/cql3/functions/types/GettableByNameData.java
+++ b/src/java/org/apache/cassandra/cql3/functions/types/GettableByNameData.java
@@ -464,6 +464,50 @@ public interface GettableByNameData
      */
     public <K, V> Map<K, V> getMap(String name, TypeToken<K> keysType, 
TypeToken<V> valuesType);
 
+    /**
+     * Returns the value for {@code name} as a vector, represented as a Java 
list.
+     *
+     * <p>This method uses the {@link CodecRegistry} to find a codec to 
convert the underlying CQL
+     * type to a vector of the specified type.
+     *
+     * <p>If the type of the elements is generic, use {@link 
#getVector(String, TypeToken)}.
+     *
+     * <p>Implementation note: the actual {@link List} implementation 
representing the vector will depend on the {@link
+     * TypeCodec codec} being used; therefore, callers should make no 
assumptions concerning its mutability nor its
+     * thread-safety.
+     *
+     * @param name          the name to retrieve.
+     * @param elementsClass the class for the elements of the vector to 
retrieve.
+     * @return the value of the {@code i}th element as a vector of {@code T} 
objects.
+     * @throws IllegalArgumentException if {@code name} is not valid name for 
this object.
+     * @throws CodecNotFoundException   if there is no registered codec to 
convert the underlying CQL type to a vector.
+     */
+    public <T> List<T> getVector(String name, Class<T> elementsClass);
+
+    /**
+     * Returns the value for {@code name} as a vector, represented as a Java 
list.
+     *
+     * <p>This method uses the {@link CodecRegistry} to find a codec to 
convert the underlying CQL
+     * type to a vector of the specified type.
+     *
+     * <p>Use this variant with nested types, which produce a generic element 
type:
+     *
+     * <pre>
+     * {@code List<List<String>> l = row.getVector("theColumn", new 
TypeToken<List<String>>() {});}
+     * </pre>
+     *
+     * <p>Implementation note: the actual {@link List} implementation 
representing the vector will depend on the {@link
+     * TypeCodec codec} being used; therefore, callers should make no 
assumptions concerning its mutability nor its
+     * thread-safety.
+     *
+     * @param name         the name to retrieve.
+     * @param elementsType the type for the elements of the vector to retrieve.
+     * @return the value of the {@code i}th element as a vector of {@code T} 
objects.
+     * @throws IllegalArgumentException if {@code name} is not valid name for 
this object.
+     * @throws CodecNotFoundException   if there is no registered codec to 
convert the underlying CQL type to a vector.
+     */
+    public <T> List<T> getVector(String name, TypeToken<T> elementsType);
+
     /**
      * Return the value for {@code name} as a UDT value.
      *
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/SettableByIndexData.java 
b/src/java/org/apache/cassandra/cql3/functions/types/SettableByIndexData.java
index a9d08981e7..4e59a27550 100644
--- 
a/src/java/org/apache/cassandra/cql3/functions/types/SettableByIndexData.java
+++ 
b/src/java/org/apache/cassandra/cql3/functions/types/SettableByIndexData.java
@@ -485,6 +485,20 @@ public interface SettableByIndexData<T extends 
SettableByIndexData<T>>
      */
     public <E> T setSet(int i, Set<E> v, TypeToken<E> elementsType);
 
+    /**
+     * Sets the {@code i}th value to the provided vector value.
+     *
+     * <p>This method uses the {@link CodecRegistry} to find a codec to handle 
the conversion of {@code v} to the
+     * underlying CQL type.
+     *
+     * @param i the index of the value to set.
+     * @param v the value to set.
+     * @return this object.
+     * @throws IndexOutOfBoundsException if {@code i} is not a valid index for 
this object.
+     * @throws CodecNotFoundException    if there is no registered codec to 
convert the value to the underlying CQL type.
+     */
+    public <E> T setVector(int i, List<E> v);
+
     /**
      * Sets the {@code i}th value to the provided UDT value.
      *
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/SettableByNameData.java 
b/src/java/org/apache/cassandra/cql3/functions/types/SettableByNameData.java
index 514ba60154..376cf471cd 100644
--- a/src/java/org/apache/cassandra/cql3/functions/types/SettableByNameData.java
+++ b/src/java/org/apache/cassandra/cql3/functions/types/SettableByNameData.java
@@ -513,6 +513,20 @@ public interface SettableByNameData<T extends 
SettableData<T>>
      */
     public <E> T setSet(String name, Set<E> v, TypeToken<E> elementsType);
 
+    /**
+     * Sets the value for (all occurrences of) variable {@code name} to the 
provided vector value.
+     *
+     * <p>This method uses the {@link CodecRegistry} to find a codec to handle 
the conversion of {@code v} to the
+     * underlying CQL type.
+     *
+     * @param name the name of the value to set; if {@code name} is present 
multiple times, all its values are set.
+     * @param v    the value to set.
+     * @return this object.
+     * @throws IllegalArgumentException if {@code name} is not a valid name 
for this object.
+     * @throws CodecNotFoundException   if there is no registered codec to 
convert the value to the underlying CQL type.
+     */
+    public <E> T setVector(String name, List<E> v);
+
     /**
      * Sets the value for (all occurrences of) variable {@code name} to the 
provided UDT value.
      *
diff --git a/src/java/org/apache/cassandra/cql3/functions/types/TypeCodec.java 
b/src/java/org/apache/cassandra/cql3/functions/types/TypeCodec.java
index 54ff540789..1212e8e2ab 100644
--- a/src/java/org/apache/cassandra/cql3/functions/types/TypeCodec.java
+++ b/src/java/org/apache/cassandra/cql3/functions/types/TypeCodec.java
@@ -122,6 +122,7 @@ import static 
org.apache.cassandra.cql3.functions.types.DataType.*;
  */
 public abstract class TypeCodec<T>
 {
+    private final static int VARIABLE_LENGTH = -1;
 
     /**
      * Return the default codec for the CQL type {@code boolean}. The returned 
codec maps the CQL type
@@ -375,6 +376,19 @@ public abstract class TypeCodec<T>
         return new MapCodec<>(keyCodec, valueCodec);
     }
 
+    /**
+     * Return a newly-created codec for the given CQL vector type. The 
returned codec maps the vector
+     * type into the Java type {@link List}. This method does not cache 
returned instances and
+     * returns a newly-allocated object at each invocation.
+     *
+     * @param type the vector type this codec should handle.
+     * @return A newly-created codec for the given CQL tuple type.
+     */
+    public static <E> TypeCodec<List<E>> vector(VectorType type, TypeCodec<E> 
valueCodec)
+    {
+        return VectorCodec.of(type, valueCodec);
+    }
+
     /**
      * Return a newly-created codec for the given user-defined CQL type. The 
returned codec maps the
      * user-defined type into the Java type {@link UDTValue}. This method does 
not cache returned
@@ -488,6 +502,24 @@ public abstract class TypeCodec<T>
         return cqlType;
     }
 
+    /**
+     * @return the length of values for this type if all values are of fixed 
length, {@link #VARIABLE_LENGTH} otherwise
+     */
+    public int serializedSize()
+    {
+        return VARIABLE_LENGTH;
+    }
+
+    /**
+     * Checks if all values are of fixed length.
+     *
+     * @return {@code true} if all values are of fixed length, {@code false} 
otherwise.
+     */
+    public final boolean isSerializedSizeFixed()
+    {
+        return serializedSize() != VARIABLE_LENGTH;
+    }
+
     /**
      * Serialize the given value according to the CQL type handled by this 
codec.
      *
@@ -1020,6 +1052,12 @@ public abstract class TypeCodec<T>
             super(cqlType);
         }
 
+        @Override
+        public int serializedSize()
+        {
+            return 8;
+        }
+
         @Override
         public Long parse(String value)
         {
@@ -1190,6 +1228,12 @@ public abstract class TypeCodec<T>
             super(DataType.cboolean());
         }
 
+        @Override
+        public int serializedSize()
+        {
+            return 1;
+        }
+
         @Override
         public Boolean parse(String value)
         {
@@ -1308,6 +1352,12 @@ public abstract class TypeCodec<T>
             super(DataType.cdouble());
         }
 
+        @Override
+        public int serializedSize()
+        {
+            return 8;
+        }
+
         @Override
         public Double parse(String value)
         {
@@ -1364,6 +1414,12 @@ public abstract class TypeCodec<T>
             super(DataType.cfloat());
         }
 
+        @Override
+        public int serializedSize()
+        {
+            return 4;
+        }
+
         @Override
         public Float parse(String value)
         {
@@ -1593,6 +1649,12 @@ public abstract class TypeCodec<T>
             super(DataType.cint());
         }
 
+        @Override
+        public int serializedSize()
+        {
+            return 4;
+        }
+
         @Override
         public Integer parse(String value)
         {
@@ -1649,6 +1711,12 @@ public abstract class TypeCodec<T>
             super(DataType.timestamp(), Date.class);
         }
 
+        @Override
+        public int serializedSize()
+        {
+            return 8;
+        }
+
         @Override
         public Date parse(String value)
         {
@@ -1719,6 +1787,12 @@ public abstract class TypeCodec<T>
             super(DataType.date(), LocalDate.class);
         }
 
+        @Override
+        public int serializedSize()
+        {
+            return 8;
+        }
+
         @Override
         public LocalDate parse(String value)
         {
@@ -1856,6 +1930,12 @@ public abstract class TypeCodec<T>
             super(cqlType, UUID.class);
         }
 
+        @Override
+        public int serializedSize()
+        {
+            return 16;
+        }
+
         @Override
         public UUID parse(String value)
         {
diff --git a/src/java/org/apache/cassandra/cql3/functions/types/TypeTokens.java 
b/src/java/org/apache/cassandra/cql3/functions/types/TypeTokens.java
index aba74668a0..024c55e6c6 100644
--- a/src/java/org/apache/cassandra/cql3/functions/types/TypeTokens.java
+++ b/src/java/org/apache/cassandra/cql3/functions/types/TypeTokens.java
@@ -154,4 +154,23 @@ public final class TypeTokens
          }, valueType);
         // @formatter:on
     }
+
+    /**
+     * Create a {@link TypeToken} that represents a {@link TypeToken} whose 
elements are of the given type.
+     *
+     * @param eltType The vector element type.
+     * @param <T>     The vector element type.
+     * @return A {@link TypeToken} that represents a {@link TypeToken} whose 
elements are of the given
+     * type.
+     */
+    public static <T> TypeToken<List<T>> vectorOf(TypeToken<T> eltType)
+    {
+        // @formatter:off
+        return new TypeToken<List<T>>()
+        {
+        }.where(new TypeParameter<T>()
+        {
+        }, eltType);
+        // @formatter:on
+    }
 }
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/types/VectorCodec.java 
b/src/java/org/apache/cassandra/cql3/functions/types/VectorCodec.java
new file mode 100644
index 0000000000..c54b8da7b8
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/functions/types/VectorCodec.java
@@ -0,0 +1,204 @@
+/*
+ * 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.functions.types;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import 
org.apache.cassandra.cql3.functions.types.exceptions.InvalidTypeException;
+import org.apache.cassandra.transport.ProtocolVersion;
+import org.apache.cassandra.utils.vint.VIntCoding;
+
+/**
+ * {@link TypeCodec} for the CQL type {@code vector}. Vectors are represented 
as {@link List}s for convenience, since
+ * it's probably easier for UDFs trying to return a newly created vector to 
create it as a standard Java list, rather
+ * than using a custom, not-standard vector class.
+ *
+ * @param <E> The type of the vector elements.
+ */
+public abstract class VectorCodec<E> extends TypeCodec<List<E>>
+{
+    protected final VectorType type;
+    protected final TypeCodec<E> subtypeCodec;
+
+    private VectorCodec(VectorType type, TypeCodec<E> subtypeCodec)
+    {
+        super(type, TypeTokens.vectorOf(subtypeCodec.getJavaType()));
+        this.type = type;
+        this.subtypeCodec = subtypeCodec;
+    }
+
+    public static <E> VectorCodec<E> of(VectorType type, TypeCodec<E> 
subtypeCodec)
+    {
+        return subtypeCodec.isSerializedSizeFixed()
+               ? new FixedLength<>(type, subtypeCodec)
+               : new VariableLength<>(type, subtypeCodec);
+    }
+
+    @Override
+    public List<E> parse(String value) throws InvalidTypeException
+    {
+        if (value == null || value.isEmpty() || value.equalsIgnoreCase("NULL"))
+            return null;
+
+        ImmutableList.Builder<E> values = ImmutableList.builder();
+        for (String element : Splitter.on(", ").split(value.substring(1, 
value.length() - 1)))
+        {
+            values.add(subtypeCodec.parse(element));
+        }
+
+        return values.build();
+    }
+
+    @Override
+    public String format(List<E> value) throws InvalidTypeException
+    {
+        return value == null ? "NULL" : Iterables.toString(value);
+    }
+
+    /**
+     * {@link VectorCodec} for vectors of elements using a fixed-length 
encoding.
+     */
+    private static class FixedLength<E> extends VectorCodec<E>
+    {
+        private final int valueLength;
+
+        public FixedLength(VectorType type, TypeCodec<E> subtypeCodec)
+        {
+            super(type, subtypeCodec);
+            valueLength = subtypeCodec.serializedSize() * type.getDimensions();
+        }
+
+        @Override
+        public int serializedSize()
+        {
+            return valueLength;
+        }
+
+        @Override
+        public ByteBuffer serialize(List<E> value, ProtocolVersion 
protocolVersion) throws InvalidTypeException
+        {
+            if (value == null || type.getDimensions() <= 0)
+                return null;
+
+            Iterator<E> values = value.iterator();
+            ByteBuffer rv = ByteBuffer.allocate(valueLength);
+            for (int i = 0; i < type.getDimensions(); ++i)
+            {
+                ByteBuffer valueBuff = subtypeCodec.serialize(values.next(), 
protocolVersion);
+                valueBuff.rewind();
+                rv.put(valueBuff);
+            }
+            rv.flip();
+            return rv;
+        }
+
+        @Override
+        public List<E> deserialize(ByteBuffer bytes, ProtocolVersion 
protocolVersion) throws InvalidTypeException
+        {
+            if (bytes == null || bytes.remaining() == 0)
+                return null;
+
+            // Determine element size by dividing count of remaining bytes by 
number of elements.
+            // This should have a remainder of zero since all elements are of 
the same fixed size.
+            int elementSize = Math.floorDiv(bytes.remaining(), 
type.getDimensions());
+            assert bytes.remaining() % type.getDimensions() == 0
+            : String.format("Expected elements of uniform size, observed %d 
elements with total bytes %d",
+                            type.getDimensions(), bytes.remaining());
+
+            ImmutableList.Builder<E> values = ImmutableList.builder();
+            for (int i = 0; i < type.getDimensions(); ++i)
+            {
+                ByteBuffer slice = bytes.slice();
+                slice.limit(elementSize);
+                values.add(subtypeCodec.deserialize(slice, protocolVersion));
+                bytes.position(bytes.position() + elementSize);
+            }
+
+            // Restore the input ByteBuffer to its original state
+            bytes.rewind();
+
+            return values.build();
+        }
+    }
+
+    /**
+     * {@link VectorCodec} for vectors of elements using a varaible-length 
encoding.
+     */
+    private static class VariableLength<E> extends VectorCodec<E>
+    {
+        public VariableLength(VectorType type, TypeCodec<E> subtypeCodec)
+        {
+            super(type, subtypeCodec);
+        }
+
+        @Override
+        public ByteBuffer serialize(List<E> values, ProtocolVersion version) 
throws InvalidTypeException
+        {
+            if (values == null)
+                return null;
+
+            assert values.size() == type.getDimensions();
+
+            int i = 0;
+            int outputSize = 0;
+            ByteBuffer[] buffers = new ByteBuffer[values.size()];
+            for (E value : values)
+            {
+                ByteBuffer bb = subtypeCodec.serialize(value, version);
+                buffers[i++] = bb;
+                int elemSize = bb.remaining();
+                outputSize += elemSize + 
VIntCoding.computeUnsignedVIntSize(elemSize);
+            }
+
+            ByteBuffer output = ByteBuffer.allocate(outputSize);
+            for (ByteBuffer bb : buffers)
+            {
+                VIntCoding.writeUnsignedVInt32(bb.remaining(), output);
+                output.put(bb.duplicate());
+            }
+            return (ByteBuffer) output.flip();
+        }
+
+        @Override
+        public List<E> deserialize(ByteBuffer bytes, ProtocolVersion version) 
throws InvalidTypeException
+        {
+            if (bytes == null || bytes.remaining() == 0)
+                return null;
+
+            ByteBuffer input = bytes.duplicate();
+            ImmutableList.Builder<E> values = ImmutableList.builder();
+
+            for (int i = 0; i < type.getDimensions(); i++)
+            {
+                int size = VIntCoding.getUnsignedVInt32(input, 
input.position());
+                input.position(input.position() + 
VIntCoding.computeUnsignedVIntSize(size));
+
+                ByteBuffer value = size < 0 ? null : 
CodecUtils.readBytes(input, size);
+                values.add(subtypeCodec.deserialize(value, version));
+            }
+            return values.build();
+        }
+    }
+}
diff --git a/src/java/org/apache/cassandra/cql3/functions/types/VectorType.java 
b/src/java/org/apache/cassandra/cql3/functions/types/VectorType.java
new file mode 100644
index 0000000000..8d22322424
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/functions/types/VectorType.java
@@ -0,0 +1,88 @@
+/*
+ * 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.functions.types;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A vector type.
+ */
+public class VectorType extends DataType
+{
+    private final DataType subtype;
+    private final int dimensions;
+
+    VectorType(DataType subtype, int dimensions)
+    {
+        super(Name.VECTOR);
+        assert dimensions > 0 : "vectors may only have positive dimentions; 
given " + dimensions;
+        this.subtype = subtype;
+        this.dimensions = dimensions;
+    }
+
+    public DataType getSubtype()
+    {
+        return this.subtype;
+    }
+
+    public int getDimensions()
+    {
+        return this.dimensions;
+    }
+
+    @Override
+    public List<DataType> getTypeArguments()
+    {
+        return Arrays.asList(subtype, DataType.cint());
+    }
+
+    @Override
+    public boolean isFrozen()
+    {
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        VectorType that = (VectorType) o;
+
+        if (dimensions != that.dimensions) return false;
+        if (!subtype.equals(that.subtype)) return false;
+        return name == that.name;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int result = name.hashCode();
+        result = 31 * result + dimensions;
+        result = 31 * result + subtype.hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("vector<%s, %d>", subtype, dimensions);
+    }
+}
diff --git a/src/java/org/apache/cassandra/cql3/selection/Selectable.java 
b/src/java/org/apache/cassandra/cql3/selection/Selectable.java
index dbde53fc4d..5c06da64e8 100644
--- a/src/java/org/apache/cassandra/cql3/selection/Selectable.java
+++ b/src/java/org/apache/cassandra/cql3/selection/Selectable.java
@@ -910,7 +910,8 @@ public interface Selectable extends AssignmentTestable
             }
 
             VectorType<?> vectorType = (VectorType<?>) type;
-            assert vectorType.dimension == selectables.size() : 
String.format("Unable to create a vector selector of type %s from %d elements", 
vectorType.asCQL3Type(), selectables.size());
+            if (vectorType.dimension != selectables.size())
+                throw invalidRequest("Unable to create a vector selector of 
type %s from %d elements", vectorType.asCQL3Type(), selectables.size());
 
             List<AbstractType<?>> expectedTypes = new 
ArrayList<>(selectables.size());
             for (int i = 0, m = selectables.size(); i < m; i++)
diff --git a/src/java/org/apache/cassandra/utils/JavaDriverUtils.java 
b/src/java/org/apache/cassandra/utils/JavaDriverUtils.java
index d58a26c359..6c0567d03f 100644
--- a/src/java/org/apache/cassandra/utils/JavaDriverUtils.java
+++ b/src/java/org/apache/cassandra/utils/JavaDriverUtils.java
@@ -54,21 +54,9 @@ public final class JavaDriverUtils
 
     public static TypeCodec<Object> codecFor(AbstractType<?> abstractType)
     {
-        if (containsVector(abstractType.unwrap()))
-            throw new IllegalArgumentException("Vectors are not supported on 
UDFs; given " + abstractType.asCQL3Type());
         return codecFor(driverType(abstractType));
     }
 
-    private static boolean containsVector(AbstractType<?> type)
-    {
-        if (type.isVector()) return true;
-        for (AbstractType<?> child : type.subTypes())
-        {
-            if (containsVector(child)) return true;
-        }
-        return false;
-    }
-
     public static TypeCodec<Object> codecFor(DataType dataType)
     {
         return codecRegistry.codecFor(dataType);
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java 
b/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java
index 05b353acf2..7716e8b4e5 100644
--- 
a/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/validation/entities/UFSecurityTest.java
@@ -118,7 +118,13 @@ public class UFSecurityTest extends CQLTester
                                     "     
java.lang.Runtime.getRuntime().exec(\"/tmp/foo\"); return 0d;" +
                                     "} catch (Exception t) {" +
                                     "     throw new RuntimeException(t);" +
-                                    '}'}
+                                    '}'},
+        {"org.apache.cassandra.utils.vint.VIntCoding",
+         "try {" +
+         "     
org.apache.cassandra.utils.vint.VIntCoding.computeUnsignedVIntSize(0L); return 
0d;" +
+         "} catch (Exception t) {" +
+         "     throw new RuntimeException(t);" +
+         '}'}
         };
 
         for (String[] typeAndSource : typesAndSources)
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 ab39feb952..773a08cf38 100644
--- 
a/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/validation/operations/CQLVectorTest.java
@@ -36,6 +36,8 @@ import 
org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.transport.ProtocolVersion;
 import org.assertj.core.api.Assertions;
 
+import static java.lang.String.format;
+
 public class CQLVectorTest extends CQLTester.InMemory
 {
     @Test
@@ -338,25 +340,158 @@ public class CQLVectorTest extends CQLTester.InMemory
     @Test
     public void udf() throws Throwable
     {
-        // For future authors, if this test starts to fail as vectors become 
supported in UDFs, please update this test
-        // to test the integration and remove the requirement that we reject 
UDFs all together
         createTable(KEYSPACE, "CREATE TABLE %s (pk int primary key, value 
vector<int, 2>)");
-        Assertions.assertThatThrownBy(() -> createFunction(KEYSPACE,
-                                                           "",
-                                                           "CREATE FUNCTION %s 
(x vector<int, 2>) " +
-                                                           "CALLED ON NULL 
INPUT " +
-                                                           "RETURNS 
vector<int, 2> " +
-                                                           "LANGUAGE java " +
-                                                           "AS 'return x;'"))
-                  .hasRootCauseMessage("Vectors are not supported on UDFs; 
given vector<int, 2>");
-
-        Assertions.assertThatThrownBy(() -> createFunction(KEYSPACE,
-                                                           "",
-                                                           "CREATE FUNCTION %s 
(x list<vector<int, 2>>) " +
-                                                           "CALLED ON NULL 
INPUT " +
-                                                           "RETURNS 
list<vector<int, 2>> " +
-                                                           "LANGUAGE java " +
-                                                           "AS 'return x;'"))
-                  .hasRootCauseMessage("Vectors are not supported on UDFs; 
given list<vector<int, 2>>");
+        Vector<Integer> vector = vector(1, 2);
+        execute("INSERT INTO %s (pk, value) VALUES (0, ?)", vector);
+
+        // identitiy function
+        String f = createFunction(KEYSPACE,
+                                  "",
+                                  "CREATE FUNCTION %s (x vector<int, 2>) " +
+                                  "CALLED ON NULL INPUT " +
+                                  "RETURNS vector<int, 2> " +
+                                  "LANGUAGE java " +
+                                  "AS 'return x;'");
+        assertRows(execute(format("SELECT %s(value) FROM %%s", f)), 
row(vector));
+        assertRows(execute(format("SELECT %s([2, 3]) FROM %%s", f)), 
row(vector(2, 3)));
+        assertRows(execute(format("SELECT %s(null) FROM %%s", f)), 
row((Vector<Integer>) null));
+
+        // identitiy function with nested type
+        f = createFunction(KEYSPACE,
+                           "",
+                           "CREATE FUNCTION %s (x list<vector<int, 2>>) " +
+                           "CALLED ON NULL INPUT " +
+                           "RETURNS list<vector<int, 2>> " +
+                           "LANGUAGE java " +
+                           "AS 'return x;'");
+        assertRows(execute(format("SELECT %s([value]) FROM %%s", f)), 
row(list(vector)));
+        assertRows(execute(format("SELECT %s([[2, 3]]) FROM %%s", f)), 
row(list(vector(2, 3))));
+        assertRows(execute(format("SELECT %s(null) FROM %%s", f)), 
row((Vector<Integer>) null));
+
+        // identitiy function with elements of variable length
+        f = createFunction(KEYSPACE,
+                           "",
+                           "CREATE FUNCTION %s (x vector<text, 2>) " +
+                           "CALLED ON NULL INPUT " +
+                           "RETURNS vector<text, 2> " +
+                           "LANGUAGE java " +
+                           "AS 'return x;'");
+        assertRows(execute(format("SELECT %s(['abc', 'defghij']) FROM %%s", 
f)), row(vector("abc", "defghij")));
+        assertRows(execute(format("SELECT %s(null) FROM %%s", f)), 
row((Vector<Integer>) null));
+
+        // function accessing vector argument elements
+        f = createFunction(KEYSPACE,
+                           "",
+                           "CREATE FUNCTION %s (x vector<int, 2>, i int) " +
+                           "CALLED ON NULL INPUT " +
+                           "RETURNS int " +
+                           "LANGUAGE java " +
+                           "AS 'return x == null ? null : x.get(i);'");
+        assertRows(execute(format("SELECT %s(value, 0), %<s(value, 1) FROM 
%%s", f)), row(1, 2));
+        assertRows(execute(format("SELECT %s([2, 3], 0), %<s([2, 3], 1) FROM 
%%s", f)), row(2, 3));
+        assertRows(execute(format("SELECT %s(null, 0) FROM %%s", f)), 
row((Integer) null));
+
+        // function accessing vector argument dimensions
+        f = createFunction(KEYSPACE,
+                           "",
+                           "CREATE FUNCTION %s (x vector<int, 2>) " +
+                           "CALLED ON NULL INPUT " +
+                           "RETURNS int " +
+                           "LANGUAGE java " +
+                           "AS 'return x == null ? 0 : x.size();'");
+        assertRows(execute(format("SELECT %s(value) FROM %%s", f)), row(2));
+        assertRows(execute(format("SELECT %s([2, 3]) FROM %%s", f)), row(2));
+        assertRows(execute(format("SELECT %s(null) FROM %%s", f)), row(0));
+
+        // build vector with elements of fixed length
+        f = createFunction(KEYSPACE,
+                           "",
+                           "CREATE FUNCTION %s () " +
+                           "CALLED ON NULL INPUT " +
+                           "RETURNS vector<double, 3> " +
+                           "LANGUAGE java " +
+                           "AS 'return Arrays.asList(1.3, 2.2, 3.1);'");
+        assertRows(execute(format("SELECT %s() FROM %%s", f)), row(vector(1.3, 
2.2, 3.1)));
+
+        // build vector with elements of variable length
+        f = createFunction(KEYSPACE,
+                           "",
+                           "CREATE FUNCTION %s () " +
+                           "CALLED ON NULL INPUT " +
+                           "RETURNS vector<text, 3> " +
+                           "LANGUAGE java " +
+                           "AS 'return Arrays.asList(\"a\", \"bc\", 
\"def\");'");
+        assertRows(execute(format("SELECT %s() FROM %%s", f)), row(vector("a", 
"bc", "def")));
+
+        // concat vectors, just to put it all together
+        f = createFunction(KEYSPACE,
+                           "",
+                           "CREATE FUNCTION %s (x vector<int, 2>, y 
vector<int, 2>) " +
+                           "CALLED ON NULL INPUT " +
+                           "RETURNS vector<int, 4> " +
+                           "LANGUAGE java " +
+                           "AS '" +
+                           "if (x == null || y == null) return null;" +
+                           "List<Integer> l = new ArrayList<Integer>(x); " +
+                           "l.addAll(y); " +
+                           "return l;'");
+        assertRows(execute(format("SELECT %s(value, [3, 4]) FROM %%s", f)), 
row(vector(1, 2, 3, 4)));
+        assertRows(execute(format("SELECT %s([2, 3], value) FROM %%s", f)), 
row(vector(2, 3, 1, 2)));
+        assertRows(execute(format("SELECT %s(null, null) FROM %%s", f)), 
row((Vector<Integer>) null));
+
+        // Test wrong arguments on function call
+        assertInvalidThrowMessage("cannot be passed as argument 0 of function 
" + f,
+                                  InvalidRequestException.class,
+                                  format("SELECT %s((int) 0, [3, 4]) FROM 
%%s", f));
+        assertInvalidThrowMessage("cannot be passed as argument 1 of function 
" + f,
+                                  InvalidRequestException.class,
+                                  format("SELECT %s([1, 2], (int) 0) FROM 
%%s", f));
+        assertInvalidThrowMessage("Invalid number of arguments in call to 
function " + f,
+                                  InvalidRequestException.class,
+                                  format("SELECT %s([1, 2]) FROM %%s", f));
+        assertInvalidThrowMessage("Invalid number of arguments in call to 
function " + f,
+                                  InvalidRequestException.class,
+                                  format("SELECT %s([1, 2], [3, 4], [5, 6]) 
FROM %%s", f));
+        assertInvalidThrowMessage("Unable to create a vector selector of type 
vector<int, 2> from 3 elements",
+                                  InvalidRequestException.class,
+                                  format("SELECT %s([1, 2, 3], [4, 5, 6]) FROM 
%%s", f));
+
+        // Test wrong types on function creation
+        assertInvalidThrowMessage("vectors may only have positive dimentions; 
given 0",
+                                  InvalidRequestException.class,
+                                  "CREATE FUNCTION %s (x vector<int, 0>) " +
+                                  "CALLED ON NULL INPUT " +
+                                  "RETURNS vector<int, 2> " +
+                                  "LANGUAGE java " +
+                                  "AS 'return x;'");
+        assertInvalidThrowMessage("vectors may only have positive dimentions; 
given 0",
+                                  InvalidRequestException.class,
+                                  "CREATE FUNCTION %s (x vector<int, 2>) " +
+                                  "CALLED ON NULL INPUT " +
+                                  "RETURNS vector<int, 0> " +
+                                  "LANGUAGE java " +
+                                  "AS 'return x;'");
+
+        // function reading and writing a udt vector field
+        String udt = createType("CREATE TYPE %s (v vector<int,2>)");
+        alterTable("ALTER TABLE %s ADD udt " + udt);
+        execute("INSERT INTO %s (pk, udt) VALUES (0, ?)", userType("v", 
vector));
+        f = createFunction(KEYSPACE,
+                           "",
+                           "CREATE FUNCTION %s (udt " + udt + ") " +
+                           "CALLED ON NULL INPUT " +
+                           "RETURNS " + udt + ' ' +
+                           "LANGUAGE java " +
+                           "AS '" +
+                           "if (udt == null) return null;" +
+                           "List<Integer> v = new 
ArrayList<Integer>(udt.getVector(\"v\", Integer.class));" +
+                           "v.set(0, 7);" +
+                           "return udt.setVector(\"v\", v);'");
+        assertRows(execute(format("SELECT %s(udt) FROM %%s", f)), 
row(userType("v", vector(7, 2))));
+        assertRows(execute(format("SELECT %s({v: [10, 20]}) FROM %%s", f)), 
row(userType("v", vector(7, 20))));
+        assertRows(execute(format("SELECT %s(null) FROM %%s", f)), 
row((Object) null));
+
+        // make sure the function referencing the UDT is dropped before 
dropping the UDT at cleanup
+        execute("DROP FUNCTION " + f);
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to