diff --git a/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java index f6e2a3b225..df1ac74f9b 100644 --- a/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java +++ b/vector/src/main/java/org/apache/arrow/vector/BaseFixedWidthVector.java @@ -70,6 +70,7 @@ public BaseFixedWidthVector(Field field, final BufferAllocator allocator, final refreshValueCapacity(); } + @Override public int getTypeWidth() { return typeWidth; } diff --git a/vector/src/main/java/org/apache/arrow/vector/FixedWidthVector.java b/vector/src/main/java/org/apache/arrow/vector/FixedWidthVector.java index e22a973f3b..61a5574898 100644 --- a/vector/src/main/java/org/apache/arrow/vector/FixedWidthVector.java +++ b/vector/src/main/java/org/apache/arrow/vector/FixedWidthVector.java @@ -31,4 +31,7 @@ public interface FixedWidthVector extends ElementAddressableVector { /** Zero out the underlying buffer backing this vector. */ void zeroVector(); + + /** Get the width of the type in bytes. */ + int getTypeWidth(); } diff --git a/vector/src/main/java/org/apache/arrow/vector/UuidVector.java b/vector/src/main/java/org/apache/arrow/vector/UuidVector.java new file mode 100644 index 0000000000..c662a6e064 --- /dev/null +++ b/vector/src/main/java/org/apache/arrow/vector/UuidVector.java @@ -0,0 +1,481 @@ +/* + * 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.arrow.vector; + +import static org.apache.arrow.vector.extension.UuidType.UUID_BYTE_WIDTH; + +import java.nio.ByteBuffer; +import java.util.UUID; +import org.apache.arrow.memory.ArrowBuf; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.memory.util.ArrowBufPointer; +import org.apache.arrow.memory.util.hash.ArrowBufHasher; +import org.apache.arrow.vector.complex.impl.UuidReaderImpl; +import org.apache.arrow.vector.complex.reader.FieldReader; +import org.apache.arrow.vector.extension.UuidType; +import org.apache.arrow.vector.holders.NullableUuidHolder; +import org.apache.arrow.vector.holders.UuidHolder; +import org.apache.arrow.vector.types.pojo.Field; +import org.apache.arrow.vector.types.pojo.FieldType; +import org.apache.arrow.vector.util.CallBack; +import org.apache.arrow.vector.util.TransferPair; +import org.apache.arrow.vector.util.UuidUtility; + +/** + * Vector implementation for UUID values using {@link UuidType}. + * + *
Supports setting and retrieving UUIDs with efficient storage and nullable value handling. + * + *
Usage: + * + *
{@code
+ * UuidVector vector = new UuidVector("uuid_col", allocator);
+ * vector.set(0, UUID.randomUUID());
+ * UUID value = vector.getObject(0);
+ * }
+ *
+ * @see UuidType
+ * @see UuidHolder
+ * @see NullableUuidHolder
+ */
+public class UuidVector extends ExtensionTypeVectorCreates a new underlying FixedSizeBinaryVector with 16-byte width.
+ *
+ * @param name the name of the vector
+ * @param allocator the buffer allocator
+ */
+ public UuidVector(String name, BufferAllocator allocator) {
+ super(name, allocator, new FixedSizeBinaryVector(name, allocator, UUID_BYTE_WIDTH));
+ this.field = new Field(name, FieldType.nullable(new UuidType()), null);
+ }
+
+ /**
+ * Constructs a UUID vector from a field and allocator.
+ *
+ * @param field the field definition (should contain UuidType)
+ * @param allocator the buffer allocator
+ */
+ public UuidVector(Field field, BufferAllocator allocator) {
+ super(
+ field.getName(),
+ allocator,
+ new FixedSizeBinaryVector(field.getName(), allocator, UUID_BYTE_WIDTH));
+ this.field = field;
+ }
+
+ @Override
+ public UUID getObject(int index) {
+ if (isSet(index) == 0) {
+ return null;
+ }
+ final ByteBuffer bb = ByteBuffer.wrap(getUnderlyingVector().getObject(index));
+ return new UUID(bb.getLong(), bb.getLong());
+ }
+
+ @Override
+ public int hashCode(int index) {
+ return hashCode(index, null);
+ }
+
+ @Override
+ public int hashCode(int index, ArrowBufHasher hasher) {
+ return getUnderlyingVector().hashCode(index, hasher);
+ }
+
+ /**
+ * Checks if the value at the given index is set (non-null).
+ *
+ * @param index the index to check
+ * @return 1 if the value is set, 0 if null
+ */
+ public int isSet(int index) {
+ return getUnderlyingVector().isSet(index);
+ }
+
+ /**
+ * Gets the UUID value at the given index as an ArrowBuf.
+ *
+ * @param index the index to retrieve
+ * @return a buffer slice containing the 16-byte UUID
+ * @throws IllegalStateException if the value at the index is null and null checking is enabled
+ */
+ public ArrowBuf get(int index) throws IllegalStateException {
+ if (NullCheckingForGet.NULL_CHECKING_ENABLED && this.isSet(index) == 0) {
+ throw new IllegalStateException("Value at index is null");
+ } else {
+ return getBufferSlicePostNullCheck(index);
+ }
+ }
+
+ /**
+ * Reads the UUID value at the given index into a NullableUuidHolder.
+ *
+ * @param index the index to read from
+ * @param holder the holder to populate with the UUID data
+ */
+ public void get(int index, NullableUuidHolder holder) {
+ if (NullCheckingForGet.NULL_CHECKING_ENABLED && this.isSet(index) == 0) {
+ holder.isSet = 0;
+ } else {
+ holder.isSet = 1;
+ holder.buffer = getBufferSlicePostNullCheck(index);
+ }
+ }
+
+ /**
+ * Reads the UUID value at the given index into a UuidHolder.
+ *
+ * @param index the index to read from
+ * @param holder the holder to populate with the UUID data
+ */
+ public void get(int index, UuidHolder holder) {
+ holder.isSet = 1;
+ holder.buffer = getBufferSlicePostNullCheck(index);
+ }
+
+ /**
+ * Sets the UUID value at the given index.
+ *
+ * @param index the index to set
+ * @param value the UUID value to set, or null to set a null value
+ */
+ public void set(int index, UUID value) {
+ if (value != null) {
+ set(index, UuidUtility.getBytesFromUUID(value));
+ } else {
+ getUnderlyingVector().setNull(index);
+ }
+ }
+
+ /**
+ * Sets the UUID value at the given index from a UuidHolder.
+ *
+ * @param index the index to set
+ * @param holder the holder containing the UUID data
+ */
+ public void set(int index, UuidHolder holder) {
+ this.set(index, holder.isSet, holder.buffer);
+ }
+
+ /**
+ * Sets the UUID value at the given index from a NullableUuidHolder.
+ *
+ * @param index the index to set
+ * @param holder the holder containing the UUID data
+ */
+ public void set(int index, NullableUuidHolder holder) {
+ this.set(index, holder.isSet, holder.buffer);
+ }
+
+ /**
+ * Sets the UUID value at the given index with explicit null flag.
+ *
+ * @param index the index to set
+ * @param isSet 1 if the value is set, 0 if null
+ * @param buffer the buffer containing the 16-byte UUID data
+ */
+ public void set(int index, int isSet, ArrowBuf buffer) {
+ getUnderlyingVector().set(index, isSet, buffer);
+ }
+
+ /**
+ * Sets the UUID value at the given index from an ArrowBuf.
+ *
+ * @param index the index to set
+ * @param value the buffer containing the 16-byte UUID data
+ */
+ public void set(int index, ArrowBuf value) {
+ getUnderlyingVector().set(index, value);
+ }
+
+ /**
+ * Sets the UUID value at the given index by copying from a source buffer.
+ *
+ * @param index the index to set
+ * @param source the source buffer to copy from
+ * @param sourceOffset the offset in the source buffer where the UUID data starts
+ */
+ public void set(int index, ArrowBuf source, int sourceOffset) {
+ // Copy bytes from source buffer to target vector data buffer
+ ArrowBuf dataBuffer = getUnderlyingVector().getDataBuffer();
+ dataBuffer.setBytes((long) index * UUID_BYTE_WIDTH, source, sourceOffset, UUID_BYTE_WIDTH);
+ getUnderlyingVector().setIndexDefined(index);
+ }
+
+ /**
+ * Sets the UUID value at the given index from a byte array.
+ *
+ * @param index the index to set
+ * @param value the 16-byte array containing the UUID data
+ */
+ public void set(int index, byte[] value) {
+ getUnderlyingVector().set(index, value);
+ }
+
+ /**
+ * Sets the UUID value at the given index, expanding capacity if needed.
+ *
+ * @param index the index to set
+ * @param value the UUID value to set, or null to set a null value
+ */
+ public void setSafe(int index, UUID value) {
+ if (value != null) {
+ setSafe(index, UuidUtility.getBytesFromUUID(value));
+ } else {
+ getUnderlyingVector().setNull(index);
+ }
+ }
+
+ /**
+ * Sets the UUID value at the given index from a NullableUuidHolder, expanding capacity if needed.
+ *
+ * @param index the index to set
+ * @param holder the holder containing the UUID data, or null to set a null value
+ */
+ public void setSafe(int index, NullableUuidHolder holder) {
+ if (holder != null) {
+ getUnderlyingVector().setSafe(index, holder.isSet, holder.buffer);
+ } else {
+ getUnderlyingVector().setNull(index);
+ }
+ }
+
+ /**
+ * Sets the UUID value at the given index from a UuidHolder, expanding capacity if needed.
+ *
+ * @param index the index to set
+ * @param holder the holder containing the UUID data, or null to set a null value
+ */
+ public void setSafe(int index, UuidHolder holder) {
+ if (holder != null) {
+ getUnderlyingVector().setSafe(index, holder.isSet, holder.buffer);
+ } else {
+ getUnderlyingVector().setNull(index);
+ }
+ }
+
+ /**
+ * Sets the UUID value at the given index from a byte array, expanding capacity if needed.
+ *
+ * @param index the index to set
+ * @param value the 16-byte array containing the UUID data
+ */
+ public void setSafe(int index, byte[] value) {
+ getUnderlyingVector().setIndexDefined(index);
+ getUnderlyingVector().setSafe(index, value);
+ }
+
+ /**
+ * Sets the UUID value at the given index from an ArrowBuf, expanding capacity if needed.
+ *
+ * @param index the index to set
+ * @param value the buffer containing the 16-byte UUID data
+ */
+ public void setSafe(int index, ArrowBuf value) {
+ getUnderlyingVector().setSafe(index, value);
+ }
+
+ @Override
+ public void copyFrom(int fromIndex, int thisIndex, ValueVector from) {
+ getUnderlyingVector()
+ .copyFromSafe(fromIndex, thisIndex, ((UuidVector) from).getUnderlyingVector());
+ }
+
+ @Override
+ public void copyFromSafe(int fromIndex, int thisIndex, ValueVector from) {
+ getUnderlyingVector()
+ .copyFromSafe(fromIndex, thisIndex, ((UuidVector) from).getUnderlyingVector());
+ }
+
+ @Override
+ public Field getField() {
+ return field;
+ }
+
+ @Override
+ public ArrowBufPointer getDataPointer(int i) {
+ return getUnderlyingVector().getDataPointer(i);
+ }
+
+ @Override
+ public ArrowBufPointer getDataPointer(int i, ArrowBufPointer arrowBufPointer) {
+ return getUnderlyingVector().getDataPointer(i, arrowBufPointer);
+ }
+
+ @Override
+ public void allocateNew(int valueCount) {
+ getUnderlyingVector().allocateNew(valueCount);
+ }
+
+ @Override
+ public void zeroVector() {
+ getUnderlyingVector().zeroVector();
+ }
+
+ @Override
+ public TransferPair makeTransferPair(ValueVector to) {
+ return new TransferImpl((UuidVector) to);
+ }
+
+ @Override
+ protected FieldReader getReaderImpl() {
+ return new UuidReaderImpl(this);
+ }
+
+ @Override
+ public TransferPair getTransferPair(Field field, BufferAllocator allocator) {
+ return new TransferImpl(field, allocator);
+ }
+
+ @Override
+ public TransferPair getTransferPair(Field field, BufferAllocator allocator, CallBack callBack) {
+ return getTransferPair(field, allocator);
+ }
+
+ @Override
+ public TransferPair getTransferPair(String ref, BufferAllocator allocator) {
+ return new TransferImpl(ref, allocator);
+ }
+
+ @Override
+ public TransferPair getTransferPair(String ref, BufferAllocator allocator, CallBack callBack) {
+ return getTransferPair(ref, allocator);
+ }
+
+ @Override
+ public TransferPair getTransferPair(BufferAllocator allocator) {
+ return getTransferPair(this.getField().getName(), allocator);
+ }
+
+ private ArrowBuf getBufferSlicePostNullCheck(int index) {
+ return getUnderlyingVector()
+ .getDataBuffer()
+ .slice((long) index * UUID_BYTE_WIDTH, UUID_BYTE_WIDTH);
+ }
+
+ @Override
+ public int getTypeWidth() {
+ return getUnderlyingVector().getTypeWidth();
+ }
+
+ /** {@link TransferPair} for {@link UuidVector}. */
+ public class TransferImpl implements TransferPair {
+ UuidVector to;
+
+ /**
+ * Constructs a transfer pair with the given target vector.
+ *
+ * @param to the target UUID vector
+ */
+ public TransferImpl(UuidVector to) {
+ this.to = to;
+ }
+
+ /**
+ * Constructs a transfer pair, creating a new target vector from the field and allocator.
+ *
+ * @param field the field definition for the target vector
+ * @param allocator the buffer allocator for the target vector
+ */
+ public TransferImpl(Field field, BufferAllocator allocator) {
+ this.to = new UuidVector(field, allocator);
+ }
+
+ /**
+ * Constructs a transfer pair, creating a new target vector with the given name and allocator.
+ *
+ * @param ref the name for the target vector
+ * @param allocator the buffer allocator for the target vector
+ */
+ public TransferImpl(String ref, BufferAllocator allocator) {
+ this.to = new UuidVector(ref, allocator);
+ }
+
+ /**
+ * Gets the target vector of this transfer pair.
+ *
+ * @return the target UUID vector
+ */
+ public UuidVector getTo() {
+ return this.to;
+ }
+
+ /** Transfers ownership of data from the source vector to the target vector. */
+ public void transfer() {
+ getUnderlyingVector().transferTo(to.getUnderlyingVector());
+ }
+
+ /**
+ * Splits and transfers a range of values from the source vector to the target vector.
+ *
+ * @param startIndex the starting index in the source vector
+ * @param length the number of values to transfer
+ */
+ public void splitAndTransfer(int startIndex, int length) {
+ getUnderlyingVector().splitAndTransferTo(startIndex, length, to.getUnderlyingVector());
+ }
+
+ /**
+ * Copies a value from the source vector to the target vector, expanding capacity if needed.
+ *
+ * @param fromIndex the index in the source vector
+ * @param toIndex the index in the target vector
+ */
+ public void copyValueSafe(int fromIndex, int toIndex) {
+ to.copyFromSafe(fromIndex, toIndex, (ValueVector) UuidVector.this);
+ }
+ }
+}
diff --git a/vector/src/main/java/org/apache/arrow/vector/complex/impl/ExtensionTypeWriterFactory.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/ExtensionTypeWriterFactory.java
index 09f0314c5f..a01d591555 100644
--- a/vector/src/main/java/org/apache/arrow/vector/complex/impl/ExtensionTypeWriterFactory.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/ExtensionTypeWriterFactory.java
@@ -20,8 +20,8 @@
import org.apache.arrow.vector.complex.writer.FieldWriter;
/**
- * A factory interface for creating instances of {@link ExtensionTypeWriter}. This factory allows
- * configuring writer implementations for specific {@link ExtensionTypeVector}.
+ * A factory interface for creating instances of {@link AbstractExtensionTypeWriter}. This factory
+ * allows configuring writer implementations for specific {@link ExtensionTypeVector}.
*
* @param Provides methods to read UUID values from a vector, including support for reading into {@link
+ * UuidHolder} and retrieving values as {@link java.util.UUID} objects.
+ *
+ * @see UuidVector
+ * @see org.apache.arrow.vector.extension.UuidType
+ */
public class UuidReaderImpl extends AbstractFieldReader {
private final UuidVector vector;
+ /**
+ * Constructs a reader for the given UUID vector.
+ *
+ * @param vector the UUID vector to read from
+ */
public UuidReaderImpl(UuidVector vector) {
super();
this.vector = vector;
@@ -48,12 +63,26 @@ public boolean isSet() {
@Override
public void read(ExtensionHolder holder) {
- vector.get(idx(), (UuidHolder) holder);
+ if (holder instanceof UuidHolder) {
+ vector.get(idx(), (UuidHolder) holder);
+ } else if (holder instanceof NullableUuidHolder) {
+ vector.get(idx(), (NullableUuidHolder) holder);
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported holder type for UuidReader: " + holder.getClass());
+ }
}
@Override
public void read(int arrayIndex, ExtensionHolder holder) {
- vector.get(arrayIndex, (UuidHolder) holder);
+ if (holder instanceof UuidHolder) {
+ vector.get(arrayIndex, (UuidHolder) holder);
+ } else if (holder instanceof NullableUuidHolder) {
+ vector.get(arrayIndex, (NullableUuidHolder) holder);
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported holder type for UuidReader: " + holder.getClass());
+ }
}
@Override
diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java
similarity index 72%
rename from vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java
rename to vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java
index 1b1bf4e6e4..35988129cb 100644
--- a/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterFactory.java
@@ -19,8 +19,22 @@
import org.apache.arrow.vector.ExtensionTypeVector;
import org.apache.arrow.vector.UuidVector;
+/**
+ * Factory for creating {@link UuidWriterImpl} instances.
+ *
+ * This factory is used to create writers for UUID extension type vectors.
+ *
+ * @see UuidWriterImpl
+ * @see org.apache.arrow.vector.extension.UuidType
+ */
public class UuidWriterFactory implements ExtensionTypeWriterFactory {
+ /**
+ * Creates a writer implementation for the given extension type vector.
+ *
+ * @param extensionTypeVector the vector to create a writer for
+ * @return a {@link UuidWriterImpl} if the vector is a {@link UuidVector}, null otherwise
+ */
@Override
public AbstractFieldWriter getWriterImpl(ExtensionTypeVector extensionTypeVector) {
if (extensionTypeVector instanceof UuidVector) {
diff --git a/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
similarity index 51%
rename from vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
rename to vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
index 68029b1df5..8a78add11c 100644
--- a/vector/src/test/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
+++ b/vector/src/main/java/org/apache/arrow/vector/complex/impl/UuidWriterImpl.java
@@ -16,32 +16,53 @@
*/
package org.apache.arrow.vector.complex.impl;
-import java.nio.ByteBuffer;
-import java.util.UUID;
+import org.apache.arrow.memory.ArrowBuf;
import org.apache.arrow.vector.UuidVector;
-import org.apache.arrow.vector.holder.UuidHolder;
import org.apache.arrow.vector.holders.ExtensionHolder;
+import org.apache.arrow.vector.holders.NullableUuidHolder;
+import org.apache.arrow.vector.holders.UuidHolder;
+/**
+ * Writer implementation for {@link UuidVector}.
+ *
+ * Supports writing UUID values in multiple formats: {@link java.util.UUID}, byte arrays, and
+ * {@link ArrowBuf}. Also handles {@link UuidHolder} and {@link NullableUuidHolder}.
+ *
+ * @see UuidVector
+ * @see org.apache.arrow.vector.extension.UuidType
+ */
public class UuidWriterImpl extends AbstractExtensionTypeWriter UUIDs are stored as 16-byte fixed-size binary values. This extension type provides a
+ * standardized way to represent UUIDs in Arrow, making them interoperable across different systems
+ * and languages.π
+ *
+ * The extension name is "arrow.uuid" and it uses {@link ArrowType.FixedSizeBinary} with 16 bytes
+ * as the storage type.
+ *
+ * Usage:
+ *
+ * The {@code isSet} field controls nullability: when {@code isSet = 1}, the holder contains a
+ * valid UUID in {@code buffer}; when {@code isSet = 0}, the holder represents a null value and
+ * {@code buffer} should not be accessed.
+ *
+ * @see UuidHolder
+ * @see org.apache.arrow.vector.UuidVector
+ * @see org.apache.arrow.vector.extension.UuidType
+ */
+public class NullableUuidHolder extends ExtensionHolder {
+ /** Buffer containing 16-byte UUID data. */
+ public ArrowBuf buffer;
+}
diff --git a/vector/src/test/java/org/apache/arrow/vector/holder/UuidHolder.java b/vector/src/main/java/org/apache/arrow/vector/holders/UuidHolder.java
similarity index 62%
rename from vector/src/test/java/org/apache/arrow/vector/holder/UuidHolder.java
rename to vector/src/main/java/org/apache/arrow/vector/holders/UuidHolder.java
index 207b0951a7..484e05c24b 100644
--- a/vector/src/test/java/org/apache/arrow/vector/holder/UuidHolder.java
+++ b/vector/src/main/java/org/apache/arrow/vector/holders/UuidHolder.java
@@ -14,10 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.arrow.vector.holder;
+package org.apache.arrow.vector.holders;
-import org.apache.arrow.vector.holders.ExtensionHolder;
+import org.apache.arrow.memory.ArrowBuf;
+/**
+ * Value holder for non-nullable UUID values.
+ *
+ * Contains a 16-byte UUID in {@code buffer} with {@code isSet} always 1.
+ *
+ * @see NullableUuidHolder
+ * @see org.apache.arrow.vector.UuidVector
+ * @see org.apache.arrow.vector.extension.UuidType
+ */
public class UuidHolder extends ExtensionHolder {
- public byte[] value;
+ /** Buffer containing 16-byte UUID data. */
+ public ArrowBuf buffer;
+
+ /** Constructs a UuidHolder with isSet = 1. */
+ public UuidHolder() {
+ this.isSet = 1;
+ }
}
diff --git a/vector/src/main/java/org/apache/arrow/vector/util/UuidUtility.java b/vector/src/main/java/org/apache/arrow/vector/util/UuidUtility.java
new file mode 100644
index 0000000000..a1b0b54579
--- /dev/null
+++ b/vector/src/main/java/org/apache/arrow/vector/util/UuidUtility.java
@@ -0,0 +1,77 @@
+/*
+ * 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.arrow.vector.util;
+
+import static org.apache.arrow.vector.extension.UuidType.UUID_BYTE_WIDTH;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.UUID;
+import org.apache.arrow.memory.ArrowBuf;
+
+/**
+ * Utility class for UUID conversions and operations.
+ *
+ * Provides methods to convert between {@link UUID} objects and byte representations used in
+ * Arrow vectors.
+ *
+ * @see org.apache.arrow.vector.UuidVector
+ * @see org.apache.arrow.vector.extension.UuidType
+ */
+public class UuidUtility {
+ /**
+ * Converts a UUID to a 16-byte array.
+ *
+ * The UUID is stored in big-endian byte order, with the most significant bits first.
+ *
+ * @param uuid the UUID to convert
+ * @return a 16-byte array representing the UUID
+ */
+ public static byte[] getBytesFromUUID(UUID uuid) {
+ byte[] result = new byte[16];
+ long msb = uuid.getMostSignificantBits();
+ long lsb = uuid.getLeastSignificantBits();
+ for (int i = 15; i >= 8; i--) {
+ result[i] = (byte) (lsb & 0xFF);
+ lsb >>= 8;
+ }
+ for (int i = 7; i >= 0; i--) {
+ result[i] = (byte) (msb & 0xFF);
+ msb >>= 8;
+ }
+ return result;
+ }
+
+ /**
+ * Constructs a UUID from bytes stored in an ArrowBuf at the specified index.
+ *
+ * Reads 16 bytes from the buffer starting at the given index and interprets them as a UUID in
+ * big-endian byte order.
+ *
+ * @param buffer the buffer containing UUID data
+ * @param index the byte offset in the buffer where the UUID starts
+ * @return the UUID constructed from the buffer data
+ */
+ public static UUID uuidFromArrowBuf(ArrowBuf buffer, long index) {
+ ByteBuffer buf = buffer.nioBuffer(index, UUID_BYTE_WIDTH);
+
+ buf.order(ByteOrder.BIG_ENDIAN);
+ long mostSigBits = buf.getLong(0);
+ long leastSigBits = buf.getLong(Long.BYTES);
+ return new UUID(mostSigBits, leastSigBits);
+ }
+}
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestListVector.java b/vector/src/test/java/org/apache/arrow/vector/TestListVector.java
index c6c7c5c862..41a95a8d11 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestListVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestListVector.java
@@ -24,7 +24,6 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -39,17 +38,18 @@
import org.apache.arrow.vector.complex.impl.UuidWriterFactory;
import org.apache.arrow.vector.complex.reader.FieldReader;
import org.apache.arrow.vector.complex.writer.BaseWriter.ExtensionWriter;
-import org.apache.arrow.vector.holder.UuidHolder;
+import org.apache.arrow.vector.extension.UuidType;
import org.apache.arrow.vector.holders.DurationHolder;
import org.apache.arrow.vector.holders.FixedSizeBinaryHolder;
import org.apache.arrow.vector.holders.TimeStampMilliTZHolder;
+import org.apache.arrow.vector.holders.UuidHolder;
import org.apache.arrow.vector.types.TimeUnit;
import org.apache.arrow.vector.types.Types.MinorType;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
-import org.apache.arrow.vector.types.pojo.UuidType;
import org.apache.arrow.vector.util.TransferPair;
+import org.apache.arrow.vector.util.UuidUtility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -1259,14 +1259,12 @@ public void testListVectorReaderForExtensionType() throws Exception {
FieldReader uuidReader = reader.reader();
UuidHolder holder = new UuidHolder();
uuidReader.read(holder);
- ByteBuffer bb = ByteBuffer.wrap(holder.value);
- UUID actualUuid = new UUID(bb.getLong(), bb.getLong());
+ UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
assertEquals(u1, actualUuid);
reader.next();
uuidReader = reader.reader();
uuidReader.read(holder);
- bb = ByteBuffer.wrap(holder.value);
- actualUuid = new UUID(bb.getLong(), bb.getLong());
+ actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
assertEquals(u2, actualUuid);
}
}
@@ -1302,14 +1300,12 @@ public void testCopyFromForExtensionType() throws Exception {
FieldReader uuidReader = reader.reader();
UuidHolder holder = new UuidHolder();
uuidReader.read(holder);
- ByteBuffer bb = ByteBuffer.wrap(holder.value);
- UUID actualUuid = new UUID(bb.getLong(), bb.getLong());
+ UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
assertEquals(u1, actualUuid);
reader.next();
uuidReader = reader.reader();
uuidReader.read(holder);
- bb = ByteBuffer.wrap(holder.value);
- actualUuid = new UUID(bb.getLong(), bb.getLong());
+ actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
assertEquals(u2, actualUuid);
}
}
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestMapVector.java b/vector/src/test/java/org/apache/arrow/vector/TestMapVector.java
index 8605d250fd..df8f338f45 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestMapVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestMapVector.java
@@ -24,7 +24,6 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -42,15 +41,16 @@
import org.apache.arrow.vector.complex.writer.BaseWriter.ListWriter;
import org.apache.arrow.vector.complex.writer.BaseWriter.MapWriter;
import org.apache.arrow.vector.complex.writer.FieldWriter;
-import org.apache.arrow.vector.holder.UuidHolder;
+import org.apache.arrow.vector.extension.UuidType;
import org.apache.arrow.vector.holders.FixedSizeBinaryHolder;
+import org.apache.arrow.vector.holders.UuidHolder;
import org.apache.arrow.vector.types.Types.MinorType;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
-import org.apache.arrow.vector.types.pojo.UuidType;
import org.apache.arrow.vector.util.JsonStringArrayList;
import org.apache.arrow.vector.util.TransferPair;
+import org.apache.arrow.vector.util.UuidUtility;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -1304,14 +1304,12 @@ public void testMapVectorWithExtensionType() throws Exception {
FieldReader uuidReader = mapReader.value();
UuidHolder holder = new UuidHolder();
uuidReader.read(holder);
- ByteBuffer bb = ByteBuffer.wrap(holder.value);
- UUID actualUuid = new UUID(bb.getLong(), bb.getLong());
+ UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
assertEquals(u1, actualUuid);
mapReader.next();
uuidReader = mapReader.value();
uuidReader.read(holder);
- bb = ByteBuffer.wrap(holder.value);
- actualUuid = new UUID(bb.getLong(), bb.getLong());
+ actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
assertEquals(u2, actualUuid);
}
}
@@ -1351,14 +1349,12 @@ public void testCopyFromForExtensionType() throws Exception {
FieldReader uuidReader = mapReader.value();
UuidHolder holder = new UuidHolder();
uuidReader.read(holder);
- ByteBuffer bb = ByteBuffer.wrap(holder.value);
- UUID actualUuid = new UUID(bb.getLong(), bb.getLong());
+ UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
assertEquals(u1, actualUuid);
mapReader.next();
uuidReader = mapReader.value();
uuidReader.read(holder);
- bb = ByteBuffer.wrap(holder.value);
- actualUuid = new UUID(bb.getLong(), bb.getLong());
+ actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
assertEquals(u2, actualUuid);
}
}
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestStructVector.java b/vector/src/test/java/org/apache/arrow/vector/TestStructVector.java
index d40af9ae89..b8abfe1ef6 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestStructVector.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestStructVector.java
@@ -35,6 +35,7 @@
import org.apache.arrow.vector.complex.impl.NullableStructWriter;
import org.apache.arrow.vector.complex.writer.Float8Writer;
import org.apache.arrow.vector.complex.writer.IntWriter;
+import org.apache.arrow.vector.extension.UuidType;
import org.apache.arrow.vector.holders.ComplexHolder;
import org.apache.arrow.vector.types.Types;
import org.apache.arrow.vector.types.Types.MinorType;
@@ -42,7 +43,6 @@
import org.apache.arrow.vector.types.pojo.ArrowType.Struct;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.FieldType;
-import org.apache.arrow.vector.types.pojo.UuidType;
import org.apache.arrow.vector.util.TransferPair;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestUtils.java b/vector/src/test/java/org/apache/arrow/vector/TestUtils.java
index 82295f8037..c28751aa58 100644
--- a/vector/src/test/java/org/apache/arrow/vector/TestUtils.java
+++ b/vector/src/test/java/org/apache/arrow/vector/TestUtils.java
@@ -20,6 +20,7 @@
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.vector.types.Types.MinorType;
import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.ExtensionTypeRegistry;
import org.apache.arrow.vector.types.pojo.FieldType;
public class TestUtils {
@@ -62,4 +63,14 @@ public static String generateRandomString(int length) {
}
return sb.toString();
}
+
+ /*
+ * Ensure the extension type is registered, as there might other tests trying to unregister the
+ * type. ex.: TestExtensionType#readUnderlyingType
+ */
+ public static void ensureRegistered(ArrowType.ExtensionType type) {
+ if (ExtensionTypeRegistry.lookup(type.extensionName()) == null) {
+ ExtensionTypeRegistry.register(type);
+ }
+ }
}
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestUuidType.java b/vector/src/test/java/org/apache/arrow/vector/TestUuidType.java
new file mode 100644
index 0000000000..9f7c65b82b
--- /dev/null
+++ b/vector/src/test/java/org/apache/arrow/vector/TestUuidType.java
@@ -0,0 +1,275 @@
+/*
+ * 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.arrow.vector;
+
+import static org.apache.arrow.vector.TestUtils.ensureRegistered;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.UUID;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.dictionary.DictionaryProvider;
+import org.apache.arrow.vector.extension.UuidType;
+import org.apache.arrow.vector.ipc.ArrowStreamReader;
+import org.apache.arrow.vector.ipc.ArrowStreamWriter;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.FieldType;
+import org.apache.arrow.vector.types.pojo.Schema;
+import org.apache.arrow.vector.util.UuidUtility;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class TestUuidType {
+ BufferAllocator allocator;
+
+ @BeforeEach
+ void beforeEach() {
+ allocator = new RootAllocator();
+ }
+
+ @AfterEach
+ void afterEach() {
+ allocator.close();
+ }
+
+ @Test
+ void testConstants() {
+ assertEquals("arrow.uuid", UuidType.EXTENSION_NAME);
+ assertNotNull(UuidType.INSTANCE);
+ assertNotNull(UuidType.STORAGE_TYPE);
+ assertInstanceOf(ArrowType.FixedSizeBinary.class, UuidType.STORAGE_TYPE);
+ assertEquals(
+ UuidType.UUID_BYTE_WIDTH,
+ ((ArrowType.FixedSizeBinary) UuidType.STORAGE_TYPE).getByteWidth());
+ }
+
+ @Test
+ void testStorageType() {
+ UuidType type = new UuidType();
+ assertEquals(UuidType.STORAGE_TYPE, type.storageType());
+ assertInstanceOf(ArrowType.FixedSizeBinary.class, type.storageType());
+ }
+
+ @Test
+ void testExtensionName() {
+ UuidType type = new UuidType();
+ assertEquals("arrow.uuid", type.extensionName());
+ }
+
+ @Test
+ void testExtensionEquals() {
+ UuidType type1 = new UuidType();
+ UuidType type2 = new UuidType();
+ UuidType type3 = UuidType.INSTANCE;
+
+ assertTrue(type1.extensionEquals(type2));
+ assertTrue(type1.extensionEquals(type3));
+ assertTrue(type2.extensionEquals(type3));
+ }
+
+ @Test
+ void testIsComplex() {
+ UuidType type = new UuidType();
+ assertFalse(type.isComplex());
+ }
+
+ @Test
+ void testSerialize() {
+ UuidType type = new UuidType();
+ String serialized = type.serialize();
+ assertEquals("", serialized);
+ }
+
+ @Test
+ void testDeserializeValid() {
+ UuidType type = new UuidType();
+ ArrowType storageType = new ArrowType.FixedSizeBinary(UuidType.UUID_BYTE_WIDTH);
+
+ ArrowType deserialized = assertDoesNotThrow(() -> type.deserialize(storageType, ""));
+ assertInstanceOf(UuidType.class, deserialized);
+ assertEquals(UuidType.INSTANCE, deserialized);
+ }
+
+ @Test
+ void testDeserializeInvalidStorageType() {
+ UuidType type = new UuidType();
+ ArrowType wrongStorageType = new ArrowType.FixedSizeBinary(32);
+
+ assertThrows(UnsupportedOperationException.class, () -> type.deserialize(wrongStorageType, ""));
+ }
+
+ @Test
+ void testGetNewVector() {
+ UuidType type = new UuidType();
+ try (FieldVector vector =
+ type.getNewVector("uuid_field", FieldType.nullable(type), allocator)) {
+ assertInstanceOf(UuidVector.class, vector);
+ assertEquals("uuid_field", vector.getField().getName());
+ assertEquals(type, vector.getField().getType());
+ }
+ }
+
+ @Test
+ void testVectorOperations() {
+ UuidType type = new UuidType();
+ try (FieldVector vector =
+ type.getNewVector("uuid_field", FieldType.nullable(type), allocator)) {
+ UuidVector uuidVector = (UuidVector) vector;
+
+ UUID uuid1 = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+
+ uuidVector.setSafe(0, uuid1);
+ uuidVector.setSafe(1, uuid2);
+ uuidVector.setNull(2);
+ uuidVector.setValueCount(3);
+
+ assertEquals(uuid1, uuidVector.getObject(0));
+ assertEquals(uuid2, uuidVector.getObject(1));
+ assertNull(uuidVector.getObject(2));
+ assertFalse(uuidVector.isNull(0));
+ assertFalse(uuidVector.isNull(1));
+ assertTrue(uuidVector.isNull(2));
+ }
+ }
+
+ @Test
+ void testIpcRoundTrip() {
+ UuidType type = UuidType.INSTANCE;
+ ensureRegistered(type);
+
+ Schema schema = new Schema(Collections.singletonList(Field.nullable("uuid", type)));
+ byte[] serialized = schema.serializeAsMessage();
+ Schema deserialized = Schema.deserializeMessage(ByteBuffer.wrap(serialized));
+ assertEquals(schema, deserialized);
+ }
+
+ @Test
+ void testVectorIpcRoundTrip() throws IOException {
+ UuidType type = UuidType.INSTANCE;
+ ensureRegistered(type);
+
+ UUID uuid1 = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+
+ try (FieldVector vector = type.getNewVector("field", FieldType.nullable(type), allocator)) {
+ UuidVector uuidVector = (UuidVector) vector;
+ uuidVector.setSafe(0, uuid1);
+ uuidVector.setNull(1);
+ uuidVector.setSafe(2, uuid2);
+ uuidVector.setValueCount(3);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (VectorSchemaRoot root = new VectorSchemaRoot(Collections.singletonList(uuidVector));
+ ArrowStreamWriter writer =
+ new ArrowStreamWriter(root, new DictionaryProvider.MapDictionaryProvider(), baos)) {
+ writer.start();
+ writer.writeBatch();
+ }
+
+ try (ArrowStreamReader reader =
+ new ArrowStreamReader(new ByteArrayInputStream(baos.toByteArray()), allocator)) {
+ assertTrue(reader.loadNextBatch());
+ VectorSchemaRoot root = reader.getVectorSchemaRoot();
+ assertEquals(3, root.getRowCount());
+ assertEquals(
+ new Schema(Collections.singletonList(uuidVector.getField())), root.getSchema());
+
+ UuidVector actual = assertInstanceOf(UuidVector.class, root.getVector("field"));
+ assertFalse(actual.isNull(0));
+ assertTrue(actual.isNull(1));
+ assertFalse(actual.isNull(2));
+ assertEquals(uuid1, actual.getObject(0));
+ assertNull(actual.getObject(1));
+ assertEquals(uuid2, actual.getObject(2));
+ }
+ }
+ }
+
+ @Test
+ void testVectorByteArrayOperations() {
+ UuidType type = new UuidType();
+ try (FieldVector vector =
+ type.getNewVector("uuid_field", FieldType.nullable(type), allocator)) {
+ UuidVector uuidVector = (UuidVector) vector;
+
+ UUID uuid = UUID.randomUUID();
+ byte[] uuidBytes = UuidUtility.getBytesFromUUID(uuid);
+
+ uuidVector.setSafe(0, uuidBytes);
+ uuidVector.setValueCount(1);
+
+ assertEquals(uuid, uuidVector.getObject(0));
+
+ // Verify the bytes match
+ byte[] actualBytes = new byte[UuidType.UUID_BYTE_WIDTH];
+ uuidVector.get(0).getBytes(0, actualBytes);
+ assertArrayEquals(uuidBytes, actualBytes);
+ }
+ }
+
+ @Test
+ void testGetNewVectorWithCustomFieldType() {
+ UuidType type = new UuidType();
+ FieldType fieldType = new FieldType(false, type, null);
+
+ try (FieldVector vector = type.getNewVector("non_nullable_uuid", fieldType, allocator)) {
+ assertInstanceOf(UuidVector.class, vector);
+ assertEquals("non_nullable_uuid", vector.getField().getName());
+ assertFalse(vector.getField().isNullable());
+ }
+ }
+
+ @Test
+ void testSingleton() {
+ UuidType type1 = UuidType.INSTANCE;
+ UuidType type2 = UuidType.INSTANCE;
+
+ // Same instance
+ assertSame(type1, type2);
+ assertTrue(type1.extensionEquals(type2));
+ }
+
+ @Test
+ void testUnderlyingVector() {
+ UuidType type = new UuidType();
+ try (FieldVector vector =
+ type.getNewVector("uuid_field", FieldType.nullable(type), allocator)) {
+ UuidVector uuidVector = (UuidVector) vector;
+ FixedSizeBinaryVector underlying = uuidVector.getUnderlyingVector();
+
+ assertInstanceOf(FixedSizeBinaryVector.class, underlying);
+ assertEquals(UuidType.UUID_BYTE_WIDTH, underlying.getByteWidth());
+ }
+ }
+}
diff --git a/vector/src/test/java/org/apache/arrow/vector/TestUuidVector.java b/vector/src/test/java/org/apache/arrow/vector/TestUuidVector.java
new file mode 100644
index 0000000000..3d70238ece
--- /dev/null
+++ b/vector/src/test/java/org/apache/arrow/vector/TestUuidVector.java
@@ -0,0 +1,451 @@
+/*
+ * 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.arrow.vector;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.ByteBuffer;
+import java.util.UUID;
+import org.apache.arrow.memory.ArrowBuf;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.complex.impl.UuidReaderImpl;
+import org.apache.arrow.vector.complex.impl.UuidWriterImpl;
+import org.apache.arrow.vector.extension.UuidType;
+import org.apache.arrow.vector.holders.ExtensionHolder;
+import org.apache.arrow.vector.holders.NullableUuidHolder;
+import org.apache.arrow.vector.holders.UuidHolder;
+import org.apache.arrow.vector.util.UuidUtility;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** Tests for UuidVector, UuidWriterImpl, and UuidReaderImpl. */
+class TestUuidVector {
+
+ private BufferAllocator allocator;
+
+ @BeforeEach
+ void beforeEach() {
+ allocator = new RootAllocator();
+ }
+
+ @AfterEach
+ void afterEach() {
+ allocator.close();
+ }
+
+ // ========== Writer Tests ==========
+
+ @Test
+ void testWriteToExtensionVector() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector)) {
+ UUID uuid = UUID.randomUUID();
+ ByteBuffer bb = ByteBuffer.allocate(UuidType.UUID_BYTE_WIDTH);
+ bb.putLong(uuid.getMostSignificantBits());
+ bb.putLong(uuid.getLeastSignificantBits());
+
+ // Allocate ArrowBuf for the holder
+ try (ArrowBuf buf = allocator.buffer(UuidType.UUID_BYTE_WIDTH)) {
+ buf.setBytes(0, bb.array());
+
+ UuidHolder holder = new UuidHolder();
+ holder.buffer = buf;
+
+ writer.write(holder);
+ UUID result = vector.getObject(0);
+ assertEquals(uuid, result);
+ }
+ }
+ }
+
+ @Test
+ void testWriteExtensionWithUUID() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector)) {
+ UUID uuid = UUID.randomUUID();
+ writer.setPosition(0);
+ writer.writeExtension(uuid);
+
+ UUID result = vector.getObject(0);
+ assertEquals(uuid, result);
+ assertEquals(1, vector.getValueCount());
+ }
+ }
+
+ @Test
+ void testWriteExtensionWithByteArray() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector)) {
+ UUID uuid = UUID.randomUUID();
+ byte[] uuidBytes = UuidUtility.getBytesFromUUID(uuid);
+
+ writer.setPosition(0);
+ writer.writeExtension(uuidBytes);
+
+ UUID result = vector.getObject(0);
+ assertEquals(uuid, result);
+ assertEquals(1, vector.getValueCount());
+ }
+ }
+
+ @Test
+ void testWriteExtensionWithArrowBuf() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector);
+ ArrowBuf buf = allocator.buffer(UuidType.UUID_BYTE_WIDTH)) {
+ UUID uuid = UUID.randomUUID();
+ byte[] uuidBytes = UuidUtility.getBytesFromUUID(uuid);
+ buf.setBytes(0, uuidBytes);
+
+ writer.setPosition(0);
+ writer.writeExtension(buf);
+
+ UUID result = vector.getObject(0);
+ assertEquals(uuid, result);
+ assertEquals(1, vector.getValueCount());
+ }
+ }
+
+ @Test
+ void testWriteExtensionWithUnsupportedType() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector)) {
+ writer.setPosition(0);
+
+ IllegalArgumentException exception =
+ assertThrows(IllegalArgumentException.class, () -> writer.writeExtension("invalid-type"));
+
+ assertEquals(
+ "Unsupported value type for UUID: class java.lang.String", exception.getMessage());
+ }
+ }
+
+ @Test
+ void testWriteExtensionMultipleValues() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector)) {
+ UUID uuid1 = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+ UUID uuid3 = UUID.randomUUID();
+
+ writer.setPosition(0);
+ writer.writeExtension(uuid1);
+ writer.setPosition(1);
+ writer.writeExtension(uuid2);
+ writer.setPosition(2);
+ writer.writeExtension(uuid3);
+
+ assertEquals(uuid1, vector.getObject(0));
+ assertEquals(uuid2, vector.getObject(1));
+ assertEquals(uuid3, vector.getObject(2));
+ assertEquals(3, vector.getValueCount());
+ }
+ }
+
+ @Test
+ void testWriteWithUuidHolder() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector);
+ ArrowBuf buf = allocator.buffer(UuidType.UUID_BYTE_WIDTH)) {
+ UUID uuid = UUID.randomUUID();
+ byte[] uuidBytes = UuidUtility.getBytesFromUUID(uuid);
+ buf.setBytes(0, uuidBytes);
+
+ UuidHolder holder = new UuidHolder();
+ holder.buffer = buf;
+ holder.isSet = 1;
+
+ writer.setPosition(0);
+ writer.write(holder);
+
+ UUID result = vector.getObject(0);
+ assertEquals(uuid, result);
+ assertEquals(1, vector.getValueCount());
+ }
+ }
+
+ @Test
+ void testWriteWithNullableUuidHolder() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector);
+ ArrowBuf buf = allocator.buffer(UuidType.UUID_BYTE_WIDTH)) {
+ UUID uuid = UUID.randomUUID();
+ byte[] uuidBytes = UuidUtility.getBytesFromUUID(uuid);
+ buf.setBytes(0, uuidBytes);
+
+ NullableUuidHolder holder = new NullableUuidHolder();
+ holder.buffer = buf;
+ holder.isSet = 1;
+
+ writer.setPosition(0);
+ writer.write(holder);
+
+ UUID result = vector.getObject(0);
+ assertEquals(uuid, result);
+ assertEquals(1, vector.getValueCount());
+ }
+ }
+
+ @Test
+ void testWriteWithNullableUuidHolderNull() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector)) {
+ NullableUuidHolder holder = new NullableUuidHolder();
+ holder.isSet = 0;
+
+ writer.setPosition(0);
+ writer.write(holder);
+
+ assertTrue(vector.isNull(0));
+ assertEquals(1, vector.getValueCount());
+ }
+ }
+
+ // ========== Reader Tests ==========
+
+ @Test
+ void testReaderCopyAsValueExtensionVector() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator);
+ UuidVector vectorForRead = new UuidVector("test2", allocator);
+ UuidWriterImpl writer = new UuidWriterImpl(vector)) {
+ UUID uuid = UUID.randomUUID();
+ vectorForRead.setValueCount(1);
+ vectorForRead.set(0, uuid);
+ UuidReaderImpl reader = (UuidReaderImpl) vectorForRead.getReader();
+ reader.copyAsValue(writer);
+ UuidReaderImpl reader2 = (UuidReaderImpl) vector.getReader();
+ UuidHolder holder = new UuidHolder();
+ reader2.read(0, holder);
+ UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+ assertEquals(uuid, actualUuid);
+ }
+ }
+
+ @Test
+ void testReaderReadWithUuidHolder() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UUID uuid = UUID.randomUUID();
+ vector.setSafe(0, uuid);
+ vector.setValueCount(1);
+
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+ reader.setPosition(0);
+
+ UuidHolder holder = new UuidHolder();
+ reader.read(holder);
+
+ UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+ assertEquals(uuid, actualUuid);
+ assertEquals(1, holder.isSet);
+ }
+ }
+
+ @Test
+ void testReaderReadWithNullableUuidHolder() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UUID uuid = UUID.randomUUID();
+ vector.setSafe(0, uuid);
+ vector.setValueCount(1);
+
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+ reader.setPosition(0);
+
+ NullableUuidHolder holder = new NullableUuidHolder();
+ reader.read(holder);
+
+ UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+ assertEquals(uuid, actualUuid);
+ assertEquals(1, holder.isSet);
+ }
+ }
+
+ @Test
+ void testReaderReadWithNullableUuidHolderNull() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ vector.setNull(0);
+ vector.setValueCount(1);
+
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+ reader.setPosition(0);
+
+ NullableUuidHolder holder = new NullableUuidHolder();
+ reader.read(holder);
+
+ assertEquals(0, holder.isSet);
+ }
+ }
+
+ @Test
+ void testReaderReadWithArrayIndexUuidHolder() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UUID uuid1 = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+ UUID uuid3 = UUID.randomUUID();
+
+ vector.setSafe(0, uuid1);
+ vector.setSafe(1, uuid2);
+ vector.setSafe(2, uuid3);
+ vector.setValueCount(3);
+
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+
+ UuidHolder holder = new UuidHolder();
+ reader.read(1, holder);
+
+ UUID actualUuid = UuidUtility.uuidFromArrowBuf(holder.buffer, 0);
+ assertEquals(uuid2, actualUuid);
+ assertEquals(1, holder.isSet);
+ }
+ }
+
+ @Test
+ void testReaderReadWithArrayIndexNullableUuidHolder() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UUID uuid1 = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+
+ vector.setSafe(0, uuid1);
+ vector.setNull(1);
+ vector.setSafe(2, uuid2);
+ vector.setValueCount(3);
+
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+
+ NullableUuidHolder holder1 = new NullableUuidHolder();
+ reader.read(0, holder1);
+ assertEquals(uuid1, UuidUtility.uuidFromArrowBuf(holder1.buffer, 0));
+ assertEquals(1, holder1.isSet);
+
+ NullableUuidHolder holder2 = new NullableUuidHolder();
+ reader.read(1, holder2);
+ assertEquals(0, holder2.isSet);
+
+ NullableUuidHolder holder3 = new NullableUuidHolder();
+ reader.read(2, holder3);
+ assertEquals(uuid2, UuidUtility.uuidFromArrowBuf(holder3.buffer, 0));
+ assertEquals(1, holder3.isSet);
+ }
+ }
+
+ @Test
+ void testReaderReadWithUnsupportedHolder() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UUID uuid = UUID.randomUUID();
+ vector.setSafe(0, uuid);
+ vector.setValueCount(1);
+
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+ reader.setPosition(0);
+
+ // Create a mock unsupported holder
+ ExtensionHolder unsupportedHolder = new ExtensionHolder() {};
+
+ IllegalArgumentException exception =
+ assertThrows(IllegalArgumentException.class, () -> reader.read(unsupportedHolder));
+
+ assertTrue(exception.getMessage().contains("Unsupported holder type for UuidReader"));
+ }
+ }
+
+ @Test
+ void testReaderReadWithArrayIndexUnsupportedHolder() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UUID uuid = UUID.randomUUID();
+ vector.setSafe(0, uuid);
+ vector.setValueCount(1);
+
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+
+ // Create a mock unsupported holder
+ ExtensionHolder unsupportedHolder = new ExtensionHolder() {};
+
+ IllegalArgumentException exception =
+ assertThrows(IllegalArgumentException.class, () -> reader.read(0, unsupportedHolder));
+
+ assertTrue(exception.getMessage().contains("Unsupported holder type for UuidReader"));
+ }
+ }
+
+ @Test
+ void testReaderIsSet() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UUID uuid = UUID.randomUUID();
+ vector.setSafe(0, uuid);
+ vector.setNull(1);
+ vector.setSafe(2, uuid);
+ vector.setValueCount(3);
+
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+
+ reader.setPosition(0);
+ assertTrue(reader.isSet());
+
+ reader.setPosition(1);
+ assertFalse(reader.isSet());
+
+ reader.setPosition(2);
+ assertTrue(reader.isSet());
+ }
+ }
+
+ @Test
+ void testReaderReadObject() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UUID uuid1 = UUID.randomUUID();
+ UUID uuid2 = UUID.randomUUID();
+
+ vector.setSafe(0, uuid1);
+ vector.setNull(1);
+ vector.setSafe(2, uuid2);
+ vector.setValueCount(3);
+
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+
+ reader.setPosition(0);
+ assertEquals(uuid1, reader.readObject());
+
+ reader.setPosition(1);
+ assertNull(reader.readObject());
+
+ reader.setPosition(2);
+ assertEquals(uuid2, reader.readObject());
+ }
+ }
+
+ @Test
+ void testReaderGetMinorType() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+ assertEquals(vector.getMinorType(), reader.getMinorType());
+ }
+ }
+
+ @Test
+ void testReaderGetField() throws Exception {
+ try (UuidVector vector = new UuidVector("test", allocator)) {
+ UuidReaderImpl reader = (UuidReaderImpl) vector.getReader();
+ assertEquals(vector.getField(), reader.getField());
+ assertEquals("test", reader.getField().getName());
+ }
+ }
+}
diff --git a/vector/src/test/java/org/apache/arrow/vector/UuidVector.java b/vector/src/test/java/org/apache/arrow/vector/UuidVector.java
deleted file mode 100644
index 72ba4aa555..0000000000
--- a/vector/src/test/java/org/apache/arrow/vector/UuidVector.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.arrow.vector;
-
-import java.nio.ByteBuffer;
-import java.util.UUID;
-import org.apache.arrow.memory.BufferAllocator;
-import org.apache.arrow.memory.util.hash.ArrowBufHasher;
-import org.apache.arrow.vector.complex.impl.UuidReaderImpl;
-import org.apache.arrow.vector.complex.reader.FieldReader;
-import org.apache.arrow.vector.holder.UuidHolder;
-import org.apache.arrow.vector.types.pojo.Field;
-import org.apache.arrow.vector.types.pojo.FieldType;
-import org.apache.arrow.vector.types.pojo.UuidType;
-import org.apache.arrow.vector.util.TransferPair;
-
-public class UuidVector extends ExtensionTypeVector{@code
+ * UuidVector vector = new UuidVector("uuid_col", allocator);
+ * vector.set(0, UUID.randomUUID());
+ * UUID value = vector.getObject(0);
+ * }
+ *
+ * @see UuidVector
+ * @see org.apache.arrow.vector.holders.UuidHolder
+ * @see org.apache.arrow.vector.holders.NullableUuidHolder
+ */
+public class UuidType extends ExtensionType {
+ /** Singleton instance of UuidType. */
+ public static final UuidType INSTANCE = new UuidType();
+
+ /** Extension name registered in the Arrow extension type registry. */
+ public static final String EXTENSION_NAME = "arrow.uuid";
+
+ /** Number of bytes used to store a UUID (128 bits = 16 bytes). */
+ public static final int UUID_BYTE_WIDTH = 16;
+
+ /** Number of characters in the standard UUID string representation (with hyphens). */
+ public static final int UUID_STRING_WIDTH = 36;
+
+ /** Storage type for UUID: FixedSizeBinary(16). */
+ public static final ArrowType STORAGE_TYPE = new ArrowType.FixedSizeBinary(UUID_BYTE_WIDTH);
+
+ static {
+ ExtensionTypeRegistry.register(INSTANCE);
+ }
+
+ @Override
+ public ArrowType storageType() {
+ return STORAGE_TYPE;
+ }
+
+ @Override
+ public String extensionName() {
+ return EXTENSION_NAME;
+ }
+
+ @Override
+ public boolean extensionEquals(ExtensionType other) {
+ return other instanceof UuidType;
+ }
+
+ @Override
+ public ArrowType deserialize(ArrowType storageType, String serializedData) {
+ if (!storageType.equals(storageType())) {
+ throw new UnsupportedOperationException(
+ "Cannot construct UuidType from underlying type " + storageType);
+ }
+ return INSTANCE;
+ }
+
+ @Override
+ public String serialize() {
+ return "";
+ }
+
+ @Override
+ public boolean isComplex() {
+ return false;
+ }
+
+ @Override
+ public FieldVector getNewVector(String name, FieldType fieldType, BufferAllocator allocator) {
+ return new UuidVector(
+ name, fieldType, allocator, new FixedSizeBinaryVector(name, allocator, UUID_BYTE_WIDTH));
+ }
+}
diff --git a/vector/src/main/java/org/apache/arrow/vector/holders/NullableUuidHolder.java b/vector/src/main/java/org/apache/arrow/vector/holders/NullableUuidHolder.java
new file mode 100644
index 0000000000..e5398d82cf
--- /dev/null
+++ b/vector/src/main/java/org/apache/arrow/vector/holders/NullableUuidHolder.java
@@ -0,0 +1,35 @@
+/*
+ * 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.arrow.vector.holders;
+
+import org.apache.arrow.memory.ArrowBuf;
+
+/**
+ * Value holder for nullable UUID values.
+ *
+ *