Skip to content

Commit d424eec

Browse files
authored
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.
1 parent bd488ae commit d424eec

6 files changed

Lines changed: 178 additions & 139 deletions

File tree

pinot-spi/src/main/java/org/apache/pinot/spi/data/FieldSpec.java

Lines changed: 33 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import java.util.List;
3838
import java.util.Map;
3939
import java.util.Objects;
40-
import java.util.UUID;
4140
import javax.annotation.Nullable;
4241
import org.apache.pinot.spi.utils.BooleanUtils;
4342
import org.apache.pinot.spi.utils.ByteArray;
@@ -419,8 +418,9 @@ public Object getDefaultNullValue() {
419418
return _defaultNullValue;
420419
}
421420

421+
@JsonIgnore
422422
public String getDefaultNullValueString() {
423-
return _dataType != null ? _dataType.toString(_defaultNullValue) : getStringValue(_defaultNullValue);
423+
return _dataType.toString(_defaultNullValue);
424424
}
425425

426426
/// Returns the [String] representation of the given object.
@@ -462,22 +462,13 @@ private void setDefaultNullValue(@Nullable JsonNode defaultNullValue) {
462462
@JsonIgnore
463463
public void setDefaultNullValue(@Nullable Object defaultNullValue) {
464464
if (defaultNullValue != null) {
465-
_stringDefaultNullValue = getStringDefaultNullValue(defaultNullValue);
465+
_stringDefaultNullValue = getStringValue(defaultNullValue);
466466
}
467467
if (_dataType != null) {
468468
_defaultNullValue = getDefaultNullValue(getFieldType(), _dataType, _stringDefaultNullValue);
469469
}
470470
}
471471

472-
// UUID default values must stringify to their canonical 8-4-4-4-12 form (not raw hex) so they round-trip back
473-
// through getDefaultNullValue(...); all other types keep the generic getStringValue() form.
474-
private String getStringDefaultNullValue(Object defaultNullValue) {
475-
if (_dataType == DataType.UUID && !(defaultNullValue instanceof String)) {
476-
return _dataType.toString(defaultNullValue);
477-
}
478-
return getStringValue(defaultNullValue);
479-
}
480-
481472
public static Object getDefaultNullValue(FieldType fieldType, DataType dataType,
482473
@Nullable String stringDefaultNullValue) {
483474
if (stringDefaultNullValue != null) {
@@ -748,9 +739,25 @@ public enum FieldType {
748739
DIMENSION, METRIC, TIME, DATE_TIME, COMPLEX
749740
}
750741

751-
/**
752-
* The <code>DataType</code> enum is used to demonstrate the data type of a field.
753-
*/
742+
/// The `DataType` enum represents the data type of a field.
743+
///
744+
/// A value of a given type is held in memory as a fixed Java class, and every value-handling method on this enum
745+
/// (`convert`, `equals`, `hashCode`, `compare`, `toString`) expects and produces that representation:
746+
/// - `INT` → [Integer]
747+
/// - `LONG` → [Long]
748+
/// - `FLOAT` → [Float]
749+
/// - `DOUBLE` → [Double]
750+
/// - `BIG_DECIMAL` → [BigDecimal]
751+
/// - `BOOLEAN` → [Integer] (`0` or `1`)
752+
/// - `TIMESTAMP` → [Long] (epoch millis)
753+
/// - `STRING` / `JSON` → [String]
754+
/// - `BYTES` → `byte[]`
755+
/// - `UUID` → `byte[]` (fixed 16-byte big-endian form)
756+
/// - `MAP` / `OPEN_STRUCT` → [Map]
757+
/// - `LIST` → [List]
758+
///
759+
/// `convertInternal` is the exception: it returns the internal storage form, which for `BYTES` and `UUID` is
760+
/// [ByteArray] rather than `byte[]`.
754761
@SuppressWarnings("rawtypes")
755762
public enum DataType {
756763
// LIST is for complex lists which is different from multi-value column of primitives
@@ -881,23 +888,16 @@ public Object convert(String value) {
881888
throw new IllegalStateException();
882889
}
883890
} catch (Exception e) {
884-
throw new IllegalArgumentException(
885-
"Cannot convert value: '" + value + "' to type: " + this + " (" + e.getMessage() + ")", e);
891+
throw new IllegalArgumentException("Cannot convert value: '" + value + "' to type: " + this);
886892
}
887893
}
888894

889895
public boolean equals(Object value1, Object value2) {
890-
if (this == UUID) {
891-
return UuidUtils.equals(toBytesValue(value1), toBytesValue(value2));
892-
}
893-
return this == BYTES ? Arrays.equals(toBytesValue(value1), toBytesValue(value2)) : value1.equals(value2);
896+
return this == BYTES || this == UUID ? Arrays.equals((byte[]) value1, (byte[]) value2) : value1.equals(value2);
894897
}
895898

896899
public int hashCode(Object value) {
897-
if (this == UUID) {
898-
return UuidUtils.hashCode(toBytesValue(value));
899-
}
900-
return this == BYTES ? Arrays.hashCode(toBytesValue(value)) : value.hashCode();
900+
return this == BYTES || this == UUID ? Arrays.hashCode((byte[]) value) : value.hashCode();
901901
}
902902

903903
/**
@@ -910,26 +910,23 @@ public int hashCode(Object value) {
910910
public int compare(Object value1, Object value2) {
911911
switch (this) {
912912
case INT:
913+
case BOOLEAN:
913914
return Integer.compare((int) value1, (int) value2);
914915
case LONG:
916+
case TIMESTAMP:
915917
return Long.compare((long) value1, (long) value2);
916918
case FLOAT:
917919
return Float.compare((float) value1, (float) value2);
918920
case DOUBLE:
919921
return Double.compare((double) value1, (double) value2);
920922
case BIG_DECIMAL:
921923
return ((BigDecimal) value1).compareTo((BigDecimal) value2);
922-
case BOOLEAN:
923-
return Boolean.compare((boolean) value1, (boolean) value2);
924-
case TIMESTAMP:
925-
return Long.compare((long) value1, (long) value2);
926924
case STRING:
927925
case JSON:
928926
return ((String) value1).compareTo((String) value2);
929927
case BYTES:
930-
return ByteArray.compare(toBytesValue(value1), toBytesValue(value2));
931928
case UUID:
932-
return UuidUtils.compare(toBytesValue(value1), toBytesValue(value2));
929+
return ByteArray.compare((byte[]) value1, (byte[]) value2);
933930
case MAP:
934931
case OPEN_STRUCT:
935932
case LIST:
@@ -946,14 +943,11 @@ public String toString(Object value) {
946943
if (this == BIG_DECIMAL) {
947944
return ((BigDecimal) value).toPlainString();
948945
}
949-
if (this == UUID) {
950-
if (value instanceof UUID) {
951-
return UuidUtils.toString((UUID) value);
952-
}
953-
return UuidUtils.toString(toBytesValue(value));
954-
}
955946
if (this == BYTES) {
956-
return BytesUtils.toHexString(toBytesValue(value));
947+
return BytesUtils.toHexString((byte[]) value);
948+
}
949+
if (this == UUID) {
950+
return UuidUtils.toString((byte[]) value);
957951
}
958952
if (this == MAP || this == OPEN_STRUCT || this == LIST) {
959953
try {
@@ -1000,25 +994,8 @@ public Comparable convertInternal(String value) {
1000994
throw new IllegalStateException();
1001995
}
1002996
} catch (Exception e) {
1003-
throw new IllegalArgumentException(
1004-
"Cannot convert value: '" + value + "' to type: " + this + " (" + e.getMessage() + ")", e);
1005-
}
1006-
}
1007-
1008-
private byte[] toBytesValue(Object value) {
1009-
if (value instanceof byte[]) {
1010-
return (byte[]) value;
1011-
}
1012-
if (value instanceof ByteArray) {
1013-
return ((ByteArray) value).getBytes();
1014-
}
1015-
if (value instanceof UUID) {
1016-
return UuidUtils.toBytes((UUID) value);
1017-
}
1018-
if (value instanceof String && this == UUID) {
1019-
return UuidUtils.toBytes((String) value);
997+
throw new IllegalArgumentException("Cannot convert value: '" + value + "' to type: " + this);
1020998
}
1021-
throw new IllegalArgumentException("Unsupported value for " + this + ": " + value);
1022999
}
10231000
}
10241001

0 commit comments

Comments
 (0)