This is an automated email from the ASF dual-hosted git repository.

xiangfu0 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new d424eecb91b Rework UUID type conversion to use java.util.UUID as the 
external form (#18927)
d424eecb91b is described below

commit d424eecb91b04051810b2249fab72d7899ec78cb
Author: Xiaotian (Jackie) Jiang <[email protected]>
AuthorDate: Mon Jul 6 22:31:50 2026 -0700

    Rework UUID type conversion to use java.util.UUID as the external form 
(#18927)
    
    Establish a consistent internal/external representation for the UUID type:
    
    - PinotDataType: java.util.UUID is the external form and byte[] the internal
      form. UUID.convert / UUID_ARRAY.convert return UUID / UUID[], while 
toInternal
      returns byte[] / byte[][]. UUID.convert delegates to sourceType.toUUID, 
and
      every single-value type now defines toUUID so an unsupported conversion 
(e.g.
      INT -> UUID) throws a descriptive "Cannot convert value from INT to UUID"
      instead of a confusing "There is no single-value type" error. The 
JSON->UUID
      parsing moves into JSON.toUUID. Removes the now-unused toUuidBytesArray.
    
    - FieldSpec.DataType: UUID values are represented uniformly as byte[], 
matching
      the BYTES stored type. equals / hashCode / compare / toString drop the
      toBytesValue helper and operate on byte[] directly. Adds enum-level 
Javadoc
      documenting the in-memory value representation per type.
    
    - UuidUtils.toBytes(String) accepts the dashless 32-char hex form in 
addition to
      the canonical string, so byte[] default null values (stringified as raw 
hex)
      round-trip back through FieldSpec.getDefaultNullValue.
---
 .../java/org/apache/pinot/spi/data/FieldSpec.java  |  89 +++++-------
 .../org/apache/pinot/spi/utils/PinotDataType.java  | 156 ++++++++++++---------
 .../java/org/apache/pinot/spi/utils/UuidUtils.java |  13 +-
 .../org/apache/pinot/spi/data/FieldSpecTest.java   |  22 ++-
 .../apache/pinot/spi/utils/PinotDataTypeTest.java  |  26 ++--
 .../org/apache/pinot/spi/utils/UuidUtilsTest.java  |  11 +-
 6 files changed, 178 insertions(+), 139 deletions(-)

diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/data/FieldSpec.java 
b/pinot-spi/src/main/java/org/apache/pinot/spi/data/FieldSpec.java
index 511d2674435..c5c3a491f6c 100644
--- a/pinot-spi/src/main/java/org/apache/pinot/spi/data/FieldSpec.java
+++ b/pinot-spi/src/main/java/org/apache/pinot/spi/data/FieldSpec.java
@@ -37,7 +37,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.UUID;
 import javax.annotation.Nullable;
 import org.apache.pinot.spi.utils.BooleanUtils;
 import org.apache.pinot.spi.utils.ByteArray;
@@ -419,8 +418,9 @@ public abstract class FieldSpec implements 
Comparable<FieldSpec>, Serializable {
     return _defaultNullValue;
   }
 
+  @JsonIgnore
   public String getDefaultNullValueString() {
-    return _dataType != null ? _dataType.toString(_defaultNullValue) : 
getStringValue(_defaultNullValue);
+    return _dataType.toString(_defaultNullValue);
   }
 
   /// Returns the [String] representation of the given object.
@@ -462,22 +462,13 @@ public abstract class FieldSpec implements 
Comparable<FieldSpec>, Serializable {
   @JsonIgnore
   public void setDefaultNullValue(@Nullable Object defaultNullValue) {
     if (defaultNullValue != null) {
-      _stringDefaultNullValue = getStringDefaultNullValue(defaultNullValue);
+      _stringDefaultNullValue = getStringValue(defaultNullValue);
     }
     if (_dataType != null) {
       _defaultNullValue = getDefaultNullValue(getFieldType(), _dataType, 
_stringDefaultNullValue);
     }
   }
 
-  // UUID default values must stringify to their canonical 8-4-4-4-12 form 
(not raw hex) so they round-trip back
-  // through getDefaultNullValue(...); all other types keep the generic 
getStringValue() form.
-  private String getStringDefaultNullValue(Object defaultNullValue) {
-    if (_dataType == DataType.UUID && !(defaultNullValue instanceof String)) {
-      return _dataType.toString(defaultNullValue);
-    }
-    return getStringValue(defaultNullValue);
-  }
-
   public static Object getDefaultNullValue(FieldType fieldType, DataType 
dataType,
       @Nullable String stringDefaultNullValue) {
     if (stringDefaultNullValue != null) {
@@ -748,9 +739,25 @@ public abstract class FieldSpec implements 
Comparable<FieldSpec>, Serializable {
     DIMENSION, METRIC, TIME, DATE_TIME, COMPLEX
   }
 
-  /**
-   * The <code>DataType</code> enum is used to demonstrate the data type of a 
field.
-   */
+  /// The `DataType` enum represents the data type of a field.
+  ///
+  /// A value of a given type is held in memory as a fixed Java class, and 
every value-handling method on this enum
+  /// (`convert`, `equals`, `hashCode`, `compare`, `toString`) expects and 
produces that representation:
+  /// - `INT` → [Integer]
+  /// - `LONG` → [Long]
+  /// - `FLOAT` → [Float]
+  /// - `DOUBLE` → [Double]
+  /// - `BIG_DECIMAL` → [BigDecimal]
+  /// - `BOOLEAN` → [Integer] (`0` or `1`)
+  /// - `TIMESTAMP` → [Long] (epoch millis)
+  /// - `STRING` / `JSON` → [String]
+  /// - `BYTES` → `byte[]`
+  /// - `UUID` → `byte[]` (fixed 16-byte big-endian form)
+  /// - `MAP` / `OPEN_STRUCT` → [Map]
+  /// - `LIST` → [List]
+  ///
+  /// `convertInternal` is the exception: it returns the internal storage 
form, which for `BYTES` and `UUID` is
+  /// [ByteArray] rather than `byte[]`.
   @SuppressWarnings("rawtypes")
   public enum DataType {
     // LIST is for complex lists which is different from multi-value column of 
primitives
@@ -881,23 +888,16 @@ public abstract class FieldSpec implements 
Comparable<FieldSpec>, Serializable {
             throw new IllegalStateException();
         }
       } catch (Exception e) {
-        throw new IllegalArgumentException(
-            "Cannot convert value: '" + value + "' to type: " + this + " (" + 
e.getMessage() + ")", e);
+        throw new IllegalArgumentException("Cannot convert value: '" + value + 
"' to type: " + this);
       }
     }
 
     public boolean equals(Object value1, Object value2) {
-      if (this == UUID) {
-        return UuidUtils.equals(toBytesValue(value1), toBytesValue(value2));
-      }
-      return this == BYTES ? Arrays.equals(toBytesValue(value1), 
toBytesValue(value2)) : value1.equals(value2);
+      return this == BYTES || this == UUID ? Arrays.equals((byte[]) value1, 
(byte[]) value2) : value1.equals(value2);
     }
 
     public int hashCode(Object value) {
-      if (this == UUID) {
-        return UuidUtils.hashCode(toBytesValue(value));
-      }
-      return this == BYTES ? Arrays.hashCode(toBytesValue(value)) : 
value.hashCode();
+      return this == BYTES || this == UUID ? Arrays.hashCode((byte[]) value) : 
value.hashCode();
     }
 
     /**
@@ -910,8 +910,10 @@ public abstract class FieldSpec implements 
Comparable<FieldSpec>, Serializable {
     public int compare(Object value1, Object value2) {
       switch (this) {
         case INT:
+        case BOOLEAN:
           return Integer.compare((int) value1, (int) value2);
         case LONG:
+        case TIMESTAMP:
           return Long.compare((long) value1, (long) value2);
         case FLOAT:
           return Float.compare((float) value1, (float) value2);
@@ -919,17 +921,12 @@ public abstract class FieldSpec implements 
Comparable<FieldSpec>, Serializable {
           return Double.compare((double) value1, (double) value2);
         case BIG_DECIMAL:
           return ((BigDecimal) value1).compareTo((BigDecimal) value2);
-        case BOOLEAN:
-          return Boolean.compare((boolean) value1, (boolean) value2);
-        case TIMESTAMP:
-          return Long.compare((long) value1, (long) value2);
         case STRING:
         case JSON:
           return ((String) value1).compareTo((String) value2);
         case BYTES:
-          return ByteArray.compare(toBytesValue(value1), toBytesValue(value2));
         case UUID:
-          return UuidUtils.compare(toBytesValue(value1), toBytesValue(value2));
+          return ByteArray.compare((byte[]) value1, (byte[]) value2);
         case MAP:
         case OPEN_STRUCT:
         case LIST:
@@ -946,14 +943,11 @@ public abstract class FieldSpec implements 
Comparable<FieldSpec>, Serializable {
       if (this == BIG_DECIMAL) {
         return ((BigDecimal) value).toPlainString();
       }
-      if (this == UUID) {
-        if (value instanceof UUID) {
-          return UuidUtils.toString((UUID) value);
-        }
-        return UuidUtils.toString(toBytesValue(value));
-      }
       if (this == BYTES) {
-        return BytesUtils.toHexString(toBytesValue(value));
+        return BytesUtils.toHexString((byte[]) value);
+      }
+      if (this == UUID) {
+        return UuidUtils.toString((byte[]) value);
       }
       if (this == MAP || this == OPEN_STRUCT || this == LIST) {
         try {
@@ -1000,25 +994,8 @@ public abstract class FieldSpec implements 
Comparable<FieldSpec>, Serializable {
             throw new IllegalStateException();
         }
       } catch (Exception e) {
-        throw new IllegalArgumentException(
-            "Cannot convert value: '" + value + "' to type: " + this + " (" + 
e.getMessage() + ")", e);
-      }
-    }
-
-    private byte[] toBytesValue(Object value) {
-      if (value instanceof byte[]) {
-        return (byte[]) value;
-      }
-      if (value instanceof ByteArray) {
-        return ((ByteArray) value).getBytes();
-      }
-      if (value instanceof UUID) {
-        return UuidUtils.toBytes((UUID) value);
-      }
-      if (value instanceof String && this == UUID) {
-        return UuidUtils.toBytes((String) value);
+        throw new IllegalArgumentException("Cannot convert value: '" + value + 
"' to type: " + this);
       }
-      throw new IllegalArgumentException("Unsupported value for " + this + ": 
" + value);
     }
   }
 
diff --git 
a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/PinotDataType.java 
b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/PinotDataType.java
index 15a950000e0..5f987425aa6 100644
--- a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/PinotDataType.java
+++ b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/PinotDataType.java
@@ -112,6 +112,11 @@ public enum PinotDataType {
       throw new UnsupportedOperationException("Cannot convert value from 
BOOLEAN to BYTES");
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from 
BOOLEAN to UUID");
+    }
+
     @Override
     public Boolean convert(Object value, PinotDataType sourceType) {
       return sourceType.toBoolean(value);
@@ -168,6 +173,11 @@ public enum PinotDataType {
     public byte[] toBytes(Object value) {
       throw new UnsupportedOperationException("Cannot convert value from BYTE 
to BYTES");
     }
+
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from BYTE 
to UUID");
+    }
   },
 
   CHARACTER {
@@ -215,6 +225,11 @@ public enum PinotDataType {
     public byte[] toBytes(Object value) {
       throw new UnsupportedOperationException("Cannot convert value from 
CHARACTER to BYTES");
     }
+
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from 
CHARACTER to UUID");
+    }
   },
 
   SHORT {
@@ -262,6 +277,11 @@ public enum PinotDataType {
     public byte[] toBytes(Object value) {
       throw new UnsupportedOperationException("Cannot convert value from SHORT 
to BYTES");
     }
+
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from SHORT 
to UUID");
+    }
   },
 
   INT {
@@ -310,6 +330,11 @@ public enum PinotDataType {
       throw new UnsupportedOperationException("Cannot convert value from INT 
to BYTES");
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from INT 
to UUID");
+    }
+
     @Override
     public Integer convert(Object value, PinotDataType sourceType) {
       return sourceType.toInt(value);
@@ -365,6 +390,11 @@ public enum PinotDataType {
       throw new UnsupportedOperationException("Cannot convert value from LONG 
to BYTES");
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from LONG 
to UUID");
+    }
+
     @Override
     public Long convert(Object value, PinotDataType sourceType) {
       return sourceType.toLong(value);
@@ -419,6 +449,11 @@ public enum PinotDataType {
       throw new UnsupportedOperationException("Cannot convert value from FLOAT 
to BYTES");
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from FLOAT 
to UUID");
+    }
+
     @Override
     public Float convert(Object value, PinotDataType sourceType) {
       return sourceType.toFloat(value);
@@ -473,6 +508,11 @@ public enum PinotDataType {
       throw new UnsupportedOperationException("Cannot convert value from 
DOUBLE to BYTES");
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from 
DOUBLE to UUID");
+    }
+
     @Override
     public Double convert(Object value, PinotDataType sourceType) {
       return sourceType.toDouble(value);
@@ -525,6 +565,11 @@ public enum PinotDataType {
       return BigDecimalUtils.serialize((BigDecimal) value);
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from 
BIG_DECIMAL to UUID");
+    }
+
     @Override
     public BigDecimal convert(Object value, PinotDataType sourceType) {
       return sourceType.toBigDecimal(value);
@@ -588,6 +633,11 @@ public enum PinotDataType {
       throw new UnsupportedOperationException("Cannot convert value from 
TIMESTAMP to BYTES");
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from 
TIMESTAMP to UUID");
+    }
+
     @Override
     public Timestamp convert(Object value, PinotDataType sourceType) {
       return sourceType.toTimestamp(value);
@@ -654,6 +704,11 @@ public enum PinotDataType {
       throw new UnsupportedOperationException("Cannot convert value from DATE 
to BYTES");
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from DATE 
to UUID");
+    }
+
     @Override
     public LocalDate convert(Object value, PinotDataType sourceType) {
       switch (sourceType) {
@@ -741,6 +796,11 @@ public enum PinotDataType {
       throw new UnsupportedOperationException("Cannot convert value from TIME 
to BYTES");
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from TIME 
to UUID");
+    }
+
     @Override
     public LocalTime convert(Object value, PinotDataType sourceType) {
       switch (sourceType) {
@@ -890,6 +950,17 @@ public enum PinotDataType {
       }
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      // JSON-encoded UUID is a quoted JSON string; Jackson's UUIDDeserializer 
handles both the canonical-string form
+      // and 16-byte binary form natively.
+      try {
+        return JsonUtils.stringToObject(value.toString(), UUID.class);
+      } catch (IOException e) {
+        throw new RuntimeException("Cannot parse JSON value as UUID: " + 
value, e);
+      }
+    }
+
     @Override
     public String convert(Object value, PinotDataType sourceType) {
       return sourceType.toJson(value);
@@ -944,7 +1015,7 @@ public enum PinotDataType {
 
     @Override
     public UUID toUUID(Object value) {
-      return UuidUtils.toUUID(value);
+      return UuidUtils.toUUID((byte[]) value);
     }
 
     @Override
@@ -1001,52 +1072,22 @@ public enum PinotDataType {
 
     @Override
     public String toString(Object value) {
-      return UuidUtils.toUUID(value).toString();
+      return value.toString();
     }
 
     @Override
     public byte[] toBytes(Object value) {
-      return UuidUtils.toBytes(value);
+      return UuidUtils.toBytes((UUID) value);
     }
 
     @Override
     public UUID toUUID(Object value) {
-      return UuidUtils.toUUID(value);
+      return (UUID) value;
     }
 
     @Override
     public UUID convert(Object value, PinotDataType sourceType) {
-      switch (sourceType) {
-        case UUID:
-          return UuidUtils.toUUID(value);
-        case STRING:
-          return UuidUtils.toUUID(value.toString().trim());
-        case JSON:
-          try {
-            // JSON-encoded UUID is a quoted JSON string; Jackson's 
UUIDDeserializer handles both the
-            // canonical-string form and 16-byte binary form natively.
-            return JsonUtils.stringToObject(value.toString(), UUID.class);
-          } catch (IOException e) {
-            throw new RuntimeException("Cannot parse JSON value as UUID: " + 
value, e);
-          }
-        case BYTES:
-          return UuidUtils.toUUID(value);
-        case OBJECT:
-        case COLLECTION:
-        case UUID_ARRAY:
-        case BYTES_ARRAY:
-        case STRING_ARRAY:
-        case OBJECT_ARRAY:
-          // OBJECT, collections and single-element arrays can carry a UUID 
(e.g., scalar-function arguments
-          // resolved by FunctionInvoker, or an MV source feeding an SV UUID 
column); these types have meaningful
-          // toUUID implementations.
-          return sourceType.toUUID(value);
-        default:
-          // Numeric and other types have no meaningful UUID interpretation; 
fail with an explicit message instead
-          // of the confusing "There is no single-value type ..." from the 
generic single-value fallback.
-          throw new UnsupportedOperationException(
-              "Cannot convert value from " + sourceType + " to UUID. Input 
value: " + value);
-      }
+      return sourceType.toUUID(value);
     }
 
     @Override
@@ -1131,6 +1172,11 @@ public enum PinotDataType {
       return MapUtils.serializeMap((Map<String, Object>) value);
     }
 
+    @Override
+    public UUID toUUID(Object value) {
+      throw new UnsupportedOperationException("Cannot convert value from MAP 
to UUID");
+    }
+
     @Override
     public Object convert(Object value, PinotDataType sourceType) {
       switch (sourceType) {
@@ -1272,7 +1318,7 @@ public enum PinotDataType {
 
   TIMESTAMP_ARRAY {
     @Override
-    public Object convert(Object value, PinotDataType sourceType) {
+    public Timestamp[] convert(Object value, PinotDataType sourceType) {
       return sourceType.toTimestampArray(value);
     }
 
@@ -1342,13 +1388,19 @@ public enum PinotDataType {
 
   UUID_ARRAY {
     @Override
-    public byte[][] convert(Object value, PinotDataType sourceType) {
-      return sourceType.toUuidBytesArray(value);
+    public UUID[] convert(Object value, PinotDataType sourceType) {
+      return sourceType.toUuidArray(value);
     }
 
     @Override
     public byte[][] toInternal(Object value) {
-      return toUuidBytesArray(value);
+      UUID[] uuidArray = (UUID[]) value;
+      int length = uuidArray.length;
+      byte[][] bytesArray = new byte[length][];
+      for (int i = 0; i < length; i++) {
+        bytesArray[i] = UuidUtils.toBytes(uuidArray[i]);
+      }
+      return bytesArray;
     }
   },
 
@@ -1707,28 +1759,6 @@ public enum PinotDataType {
     }
   }
 
-  public byte[][] toUuidBytesArray(Object value) {
-    if (value instanceof byte[][]) {
-      byte[][] values = (byte[][]) value;
-      byte[][] uuidBytes = new byte[values.length][];
-      for (int i = 0; i < values.length; i++) {
-        uuidBytes[i] = UuidUtils.toBytes(values[i]);
-      }
-      return uuidBytes;
-    }
-    if (isSingleValue()) {
-      return new byte[][]{UuidUtils.toBytes(value)};
-    } else {
-      Object[] valueArray = toObjectArray(value);
-      int length = valueArray.length;
-      byte[][] uuidBytes = new byte[length][];
-      for (int i = 0; i < length; i++) {
-        uuidBytes[i] = UuidUtils.toBytes(valueArray[i]);
-      }
-      return uuidBytes;
-    }
-  }
-
   public LocalDate[] toLocalDateArray(Object value) {
     if (value instanceof LocalDate[]) {
       return (LocalDate[]) value;
@@ -2024,10 +2054,10 @@ public enum PinotDataType {
           return JSON;
         }
         throw new IllegalStateException("There is no multi-value type for 
JSON");
-      case UUID:
-        return fieldSpec.isSingleValueField() ? UUID : UUID_ARRAY;
       case BYTES:
         return fieldSpec.isSingleValueField() ? BYTES : BYTES_ARRAY;
+      case UUID:
+        return fieldSpec.isSingleValueField() ? UUID : UUID_ARRAY;
       case MAP:
         if (fieldSpec.isSingleValueField()) {
           return MAP;
diff --git a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/UuidUtils.java 
b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/UuidUtils.java
index 86365a49d08..2da10eea3d0 100644
--- a/pinot-spi/src/main/java/org/apache/pinot/spi/utils/UuidUtils.java
+++ b/pinot-spi/src/main/java/org/apache/pinot/spi/utils/UuidUtils.java
@@ -32,16 +32,13 @@ public class UuidUtils {
   }
 
   public static final int UUID_NUM_BYTES = 16;
-  private static final byte[] NULL_UUID_BYTES = new byte[UUID_NUM_BYTES];
 
   // Gregorian-to-Unix offset in 100-nanosecond units. Matches RFC 4122 / RFC 
9562.
   // This is the count of 100-ns intervals between 1582-10-15T00:00:00Z and 
1970-01-01T00:00:00Z.
   private static final long GREGORIAN_TO_UNIX_OFFSET_100NS = 
0x01b21dd213814000L;
 
   public static byte[] nullUuidBytes() {
-    byte[] uuidBytes = new byte[UUID_NUM_BYTES];
-    System.arraycopy(NULL_UUID_BYTES, 0, uuidBytes, 0, UUID_NUM_BYTES);
-    return uuidBytes;
+    return new byte[UUID_NUM_BYTES];
   }
 
   public static byte[] toBytes(long mostSignificantBits, long 
leastSignificantBits) {
@@ -60,6 +57,14 @@ public class UuidUtils {
     try {
       uuid = UUID.fromString(uuidString);
     } catch (Exception e) {
+      try {
+        // Try parsing the string as hex-encoded bytes
+        byte[] bytes = BytesUtils.toBytes(uuidString);
+        if (bytes.length == UUID_NUM_BYTES) {
+          return bytes;
+        }
+      } catch (Exception ignore) {
+      }
       throw new IllegalArgumentException("Invalid UUID value: '" + uuidString 
+ "'", e);
     }
     String canonical = uuid.toString();
diff --git 
a/pinot-spi/src/test/java/org/apache/pinot/spi/data/FieldSpecTest.java 
b/pinot-spi/src/test/java/org/apache/pinot/spi/data/FieldSpecTest.java
index fd47f0312dc..b156bb84865 100644
--- a/pinot-spi/src/test/java/org/apache/pinot/spi/data/FieldSpecTest.java
+++ b/pinot-spi/src/test/java/org/apache/pinot/spi/data/FieldSpecTest.java
@@ -227,6 +227,7 @@ public class FieldSpecTest {
   @Test
   public void testUUIDConversions() {
     byte[] uuidBytes = UuidUtils.toBytes(UUID_VALUE);
+    byte[] equalUuidBytes = UuidUtils.toBytes(UUID_VALUE);
     byte[] largerUuidBytes = UuidUtils.toBytes(LARGER_UUID_VALUE);
     String mixedCaseUuidValue = UUID_VALUE.toUpperCase();
     ByteArray uuidInternal = new ByteArray(uuidBytes);
@@ -236,10 +237,9 @@ public class FieldSpecTest {
     Assert.assertEquals(UUID.convertInternal(UUID_VALUE), uuidInternal);
     Assert.assertEquals(UUID.convertInternal(mixedCaseUuidValue), 
uuidInternal);
     Assert.assertEquals(UUID.toString(uuidBytes), UUID_VALUE);
-    Assert.assertEquals(UUID.toString(uuidInternal), UUID_VALUE);
-    Assert.assertTrue(UUID.equals(uuidBytes, uuidInternal));
-    Assert.assertEquals(UUID.hashCode(uuidBytes), uuidInternal.hashCode());
-    Assert.assertEquals(UUID.compare(uuidBytes, uuidInternal), 0);
+    Assert.assertTrue(UUID.equals(uuidBytes, equalUuidBytes));
+    Assert.assertEquals(UUID.hashCode(uuidBytes), 
UUID.hashCode(equalUuidBytes));
+    Assert.assertEquals(UUID.compare(uuidBytes, equalUuidBytes), 0);
     Assert.assertTrue(UUID.compare(uuidBytes, largerUuidBytes) < 0);
     Assert.assertTrue(UUID.compare(largerUuidBytes, uuidBytes) > 0);
   }
@@ -248,7 +248,19 @@ public class FieldSpecTest {
   public void testInvalidUUIDConversion() {
     IllegalArgumentException exception = 
Assert.expectThrows(IllegalArgumentException.class,
         () -> UUID.convert("not-a-uuid"));
-    assertThat(exception).hasMessageContaining("Invalid UUID");
+    assertThat(exception).hasMessageContaining("Cannot convert value: 
'not-a-uuid' to type: UUID");
+  }
+
+  @Test
+  public void testCompare() {
+    // BOOLEAN values are held as Integer (0/1) and TIMESTAMP as Long (epoch 
millis), so compare dispatches them
+    // through the INT and LONG cases respectively.
+    Assert.assertTrue(BOOLEAN.compare(0, 1) < 0);
+    Assert.assertTrue(BOOLEAN.compare(1, 0) > 0);
+    Assert.assertEquals(BOOLEAN.compare(1, 1), 0);
+    Assert.assertTrue(TIMESTAMP.compare(1000L, 2000L) < 0);
+    Assert.assertTrue(TIMESTAMP.compare(2000L, 1000L) > 0);
+    Assert.assertEquals(TIMESTAMP.compare(1000L, 1000L), 0);
   }
 
   /**
diff --git 
a/pinot-spi/src/test/java/org/apache/pinot/spi/utils/PinotDataTypeTest.java 
b/pinot-spi/src/test/java/org/apache/pinot/spi/utils/PinotDataTypeTest.java
index fe6f86c1fb0..9d8b65697ca 100644
--- a/pinot-spi/src/test/java/org/apache/pinot/spi/utils/PinotDataTypeTest.java
+++ b/pinot-spi/src/test/java/org/apache/pinot/spi/utils/PinotDataTypeTest.java
@@ -160,14 +160,16 @@ public class PinotDataTypeTest {
         {BYTES_ARRAY, BYTES_ARRAY, new byte[][] { "foo".getBytes(UTF_8), 
"bar".getBytes(UTF_8) },
             new byte[][] { "foo".getBytes(UTF_8), "bar".getBytes(UTF_8) }},
         {STRING_ARRAY, UUID_ARRAY, new String[] {UUID_VALUE},
-            new byte[][] {UuidUtils.toBytes(UUID_VALUE)}},
+            new java.util.UUID[] {java.util.UUID.fromString(UUID_VALUE)}},
         {UUID_ARRAY, UUID_ARRAY, new java.util.UUID[] 
{java.util.UUID.fromString(UUID_VALUE)},
-            new byte[][] {UuidUtils.toBytes(UUID_VALUE)}},
+            new java.util.UUID[] {java.util.UUID.fromString(UUID_VALUE)}},
         {COLLECTION, STRING_ARRAY, Arrays.asList("test1", "test2"), new 
String[] {"test1", "test2"}},
-        {COLLECTION, UUID_ARRAY, Arrays.asList(UUID_VALUE), new byte[][] 
{UuidUtils.toBytes(UUID_VALUE)}},
+        {COLLECTION, UUID_ARRAY, Arrays.asList(UUID_VALUE),
+            new java.util.UUID[] {java.util.UUID.fromString(UUID_VALUE)}},
         {COLLECTION, FLOAT_ARRAY, Arrays.asList(1.0f, 2.0f), new Float[] 
{1.0f, 2.0f}},
         {OBJECT_ARRAY, STRING_ARRAY, new Object[] {"test1", "test2"}, new 
String[] {"test1", "test2"}},
-        {OBJECT_ARRAY, UUID_ARRAY, new Object[] {UUID_VALUE}, new byte[][] 
{UuidUtils.toBytes(UUID_VALUE)}},
+        {OBJECT_ARRAY, UUID_ARRAY, new Object[] {UUID_VALUE},
+            new java.util.UUID[] {java.util.UUID.fromString(UUID_VALUE)}},
         {OBJECT_ARRAY, FLOAT_ARRAY, new Object[] {1.0f, 2.0f}, new Float[] 
{1.0f, 2.0f}},
     };
   }
@@ -336,14 +338,18 @@ public class PinotDataTypeTest {
     UUID u1 = 
java.util.UUID.fromString("550e8400-e29b-41d4-a716-446655440000");
     UUID u2 = 
java.util.UUID.fromString("00000000-0000-0000-0000-000000000001");
     UUID[] uuids = {u1, u2};
-    byte[][] convertedBytes = (byte[][]) UUID_ARRAY.convert(uuids, UUID_ARRAY);
-    assertEquals(UUID.convert(convertedBytes[0], BYTES), u1);
-    assertEquals(UUID.convert(convertedBytes[1], BYTES), u2);
+    UUID[] converted = (UUID[]) UUID_ARRAY.convert(uuids, UUID_ARRAY);
+    assertEquals(converted[0], u1);
+    assertEquals(converted[1], u2);
     // STRING_ARRAY → UUID_ARRAY: per-element parse.
-    byte[][] convertedStringBytes = (byte[][]) UUID_ARRAY.convert(
+    UUID[] convertedFromStrings = (UUID[]) UUID_ARRAY.convert(
         new String[]{"550e8400-e29b-41d4-a716-446655440000", 
"00000000-0000-0000-0000-000000000001"}, STRING_ARRAY);
-    assertEquals(UUID.convert(convertedStringBytes[0], BYTES), u1);
-    assertEquals(UUID.convert(convertedStringBytes[1], BYTES), u2);
+    assertEquals(convertedFromStrings[0], u1);
+    assertEquals(convertedFromStrings[1], u2);
+    // toInternal yields the 16-byte big-endian form per element.
+    byte[][] internal = (byte[][]) UUID_ARRAY.toInternal(uuids);
+    assertEquals(UUID.convert(internal[0], BYTES), u1);
+    assertEquals(UUID.convert(internal[1], BYTES), u2);
     // STRING_ARRAY destination: canonical strings.
     assertEquals(STRING_ARRAY.convert(uuids, UUID_ARRAY),
         new String[]{"550e8400-e29b-41d4-a716-446655440000", 
"00000000-0000-0000-0000-000000000001"});
diff --git 
a/pinot-spi/src/test/java/org/apache/pinot/spi/utils/UuidUtilsTest.java 
b/pinot-spi/src/test/java/org/apache/pinot/spi/utils/UuidUtilsTest.java
index 2ccbfe52f51..731c63bfab5 100644
--- a/pinot-spi/src/test/java/org/apache/pinot/spi/utils/UuidUtilsTest.java
+++ b/pinot-spi/src/test/java/org/apache/pinot/spi/utils/UuidUtilsTest.java
@@ -41,7 +41,6 @@ public class UuidUtilsTest {
         {"550e8400-e29b-41d4-a716-44665544000"},
         {"550e8400-e29b-41d4-a716-4466554400000"},
         {"550e8400-e29b-41d4-a716-44665544000g"},
-        {"550e8400e29b41d4a716446655440000"},
         {""}
     };
   }
@@ -72,6 +71,16 @@ public class UuidUtilsTest {
     assertTrue(UuidUtils.isUuid(mixedCaseUuid));
   }
 
+  @Test
+  public void testDashlessHexStringRoundTrips() {
+    String hexUuid = "550e8400e29b41d4a716446655440000";
+
+    assertEquals(UuidUtils.toBytes(hexUuid), UuidUtils.toBytes(UUID_VALUE));
+    assertEquals(UuidUtils.toString(UuidUtils.toBytes(hexUuid)), UUID_VALUE);
+    assertEquals(UuidUtils.toUUID(hexUuid), UUID.fromString(UUID_VALUE));
+    assertTrue(UuidUtils.isUuid(hexUuid));
+  }
+
   @Test(dataProvider = "invalidUuidStrings")
   public void testRejectsInvalidUuidStrings(String invalidUuid) {
     Assert.expectThrows(IllegalArgumentException.class, () -> 
UuidUtils.toBytes(invalidUuid));


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

Reply via email to