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 d3604a585db Render UUID casts as canonical strings (#19165)
d3604a585db is described below

commit d3604a585db46dcf75691887b9e9d3c7f91721d9
Author: Xiang Fu <[email protected]>
AuthorDate: Thu Aug 6 10:33:08 2026 -0700

    Render UUID casts as canonical strings (#19165)
    
    * Render UUID casts as canonical strings
    
    * Add UUID to STRING cast coverage
    
    * Harden UUID to STRING casts
    
    * Use consistent UUID cast case scoping
    
    * Simplify UUID cast handling
    
    ---------
    
    Co-authored-by: Xiang Fu <[email protected]>
---
 .../scalar/DataTypeConversionFunctionsTest.java    |  3 +
 .../transform/function/CastTransformFunction.java  | 33 ++++++++++
 .../function/CastTransformFunctionTest.java        | 74 ++++++++++++++++++++++
 3 files changed, 110 insertions(+)

diff --git 
a/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/DataTypeConversionFunctionsTest.java
 
b/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/DataTypeConversionFunctionsTest.java
index 025e5fa2137..db17f924aa5 100644
--- 
a/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/DataTypeConversionFunctionsTest.java
+++ 
b/pinot-common/src/test/java/org/apache/pinot/common/function/scalar/DataTypeConversionFunctionsTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.pinot.common.function.scalar;
 
+import java.util.UUID;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
@@ -54,6 +55,8 @@ public class DataTypeConversionFunctionsTest {
         {10, "long", 10L},
         {10D, "float", 10F},
         {10F, "double", 10D},
+        {UUID.fromString("550e8400-e29b-41d4-a716-446655440000"), "string",
+            "550e8400-e29b-41d4-a716-446655440000"},
         {"abc1", "bytes", new byte[]{(byte) 0xab, (byte) 0xc1}},
         {new byte[]{(byte) 0xab, (byte) 0xc1}, "string", "abc1"}
     };
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/CastTransformFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/CastTransformFunction.java
index 6d61c1c99ef..efef5b2b65c 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/CastTransformFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/CastTransformFunction.java
@@ -28,6 +28,7 @@ import 
org.apache.pinot.core.operator.transform.TransformResultMetadata;
 import org.apache.pinot.spi.data.FieldSpec.DataType;
 import org.apache.pinot.spi.utils.ArrayCopyUtils;
 import org.apache.pinot.spi.utils.TimestampUtils;
+import org.apache.pinot.spi.utils.UuidUtils;
 import org.roaringbitmap.RoaringBitmap;
 
 
@@ -261,6 +262,24 @@ public class CastTransformFunction extends 
BaseTransformFunction {
           ArrayCopyUtils.copyFromTimestamp(longValues, _stringValuesSV, 
length);
           return _stringValuesSV;
         }
+        case UUID: {
+          int length = valueBlock.getNumDocs();
+          initStringValuesSV(length);
+          byte[][] uuidValues = 
_transformFunction.transformToBytesValuesSV(valueBlock);
+          RoaringBitmap nullBitmap = 
_transformFunction.getNullBitmap(valueBlock);
+          if (_nullHandlingEnabled && nullBitmap != null && 
!nullBitmap.isEmpty()) {
+            RoaringBitmapUtils.forEachUnset(length, 
nullBitmap.getIntIterator(), (from, to) -> {
+              for (int i = from; i < to; i++) {
+                _stringValuesSV[i] = UuidUtils.toString(uuidValues[i]);
+              }
+            });
+          } else {
+            for (int i = 0; i < length; i++) {
+              _stringValuesSV[i] = UuidUtils.toString(uuidValues[i]);
+            }
+          }
+          return _stringValuesSV;
+        }
         default:
           return _transformFunction.transformToStringValuesSV(valueBlock);
       }
@@ -423,6 +442,20 @@ public class CastTransformFunction extends 
BaseTransformFunction {
           long[][] longValuesMV = 
_transformFunction.transformToLongValuesMV(valueBlock);
           ArrayCopyUtils.copyFromTimestamp(longValuesMV, _stringValuesMV, 
length);
           return _stringValuesMV;
+        case UUID: {
+          length = valueBlock.getNumDocs();
+          initStringValuesMV(length);
+          byte[][][] uuidValuesMV = 
_transformFunction.transformToBytesValuesMV(valueBlock);
+          for (int i = 0; i < length; i++) {
+            int numValues = uuidValuesMV[i].length;
+            String[] stringValues = new String[numValues];
+            for (int j = 0; j < numValues; j++) {
+              stringValues[j] = UuidUtils.toString(uuidValuesMV[i][j]);
+            }
+            _stringValuesMV[i] = stringValues;
+          }
+          return _stringValuesMV;
+        }
         default:
           return _transformFunction.transformToStringValuesMV(valueBlock);
       }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/CastTransformFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/CastTransformFunctionTest.java
index 7ae8af7b713..db5fc9d0cfe 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/CastTransformFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/CastTransformFunctionTest.java
@@ -21,20 +21,94 @@ package org.apache.pinot.core.operator.transform.function;
 import java.math.BigDecimal;
 import java.sql.Timestamp;
 import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
 import org.apache.pinot.common.request.context.ExpressionContext;
+import org.apache.pinot.common.request.context.LiteralContext;
 import org.apache.pinot.common.request.context.RequestContextUtils;
+import org.apache.pinot.core.operator.transform.TransformResultMetadata;
 import org.apache.pinot.spi.data.FieldSpec;
 import org.apache.pinot.spi.utils.ArrayCopyUtils;
+import org.apache.pinot.spi.utils.CommonConstants.NullValuePlaceHolder;
+import org.apache.pinot.spi.utils.UuidUtils;
 import org.roaringbitmap.RoaringBitmap;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
 import static 
org.apache.pinot.common.function.scalar.DataTypeConversionFunctions.cast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
 
 public class CastTransformFunctionTest extends BaseTransformFunctionTest {
+  @Test
+  public void testCastUuidToString() {
+    String uuid = "550e8400-e29b-41d4-a716-446655440000";
+    ExpressionContext expression = 
RequestContextUtils.getExpression("CAST(toUuid('" + uuid + "') AS STRING)");
+    TransformFunction transformFunction = 
TransformFunctionFactory.get(expression, _dataSourceMap);
+
+    String[] expectedValues = new String[NUM_ROWS];
+    Arrays.fill(expectedValues, uuid);
+    testTransformFunction(transformFunction, expectedValues);
+  }
+
+  @Test
+  public void testCastNullableUuidToString() {
+    String uuid = "550e8400-e29b-41d4-a716-446655440000";
+    byte[][] uuidValues = new byte[NUM_ROWS][];
+    String[] expectedValues = new String[NUM_ROWS];
+    RoaringBitmap nullBitmap = new RoaringBitmap();
+    for (int i = 0; i < NUM_ROWS; i++) {
+      if (isNullRow(i)) {
+        uuidValues[i] = NullValuePlaceHolder.BYTES;
+        nullBitmap.add(i);
+      } else {
+        uuidValues[i] = UuidUtils.toBytes(uuid);
+        expectedValues[i] = uuid;
+      }
+    }
+
+    TransformFunction uuidTransformFunction = mock(TransformFunction.class);
+    when(uuidTransformFunction.getResultMetadata())
+        .thenReturn(new TransformResultMetadata(FieldSpec.DataType.UUID, true, 
false));
+    
when(uuidTransformFunction.transformToBytesValuesSV(_projectionBlock)).thenReturn(uuidValues);
+    
when(uuidTransformFunction.getNullBitmap(_projectionBlock)).thenReturn(nullBitmap);
+
+    CastTransformFunction castTransformFunction = 
createUuidCastFunction(uuidTransformFunction, true);
+    testTransformFunctionWithNull(castTransformFunction, expectedValues, 
nullBitmap);
+  }
+
+  @Test
+  public void testCastUuidMvToString() {
+    String firstUuid = "550e8400-e29b-41d4-a716-446655440000";
+    String secondUuid = "123e4567-e89b-12d3-a456-426614174000";
+    byte[][][] uuidValues = new byte[NUM_ROWS][][];
+    String[][] expectedValues = new String[NUM_ROWS][];
+    for (int i = 0; i < NUM_ROWS; i++) {
+      uuidValues[i] = new byte[][]{UuidUtils.toBytes(firstUuid), 
UuidUtils.toBytes(secondUuid)};
+      expectedValues[i] = new String[]{firstUuid, secondUuid};
+    }
+
+    TransformFunction uuidTransformFunction = mock(TransformFunction.class);
+    when(uuidTransformFunction.getResultMetadata())
+        .thenReturn(new TransformResultMetadata(FieldSpec.DataType.UUID, 
false, false));
+    
when(uuidTransformFunction.transformToBytesValuesMV(_projectionBlock)).thenReturn(uuidValues);
+
+    CastTransformFunction castTransformFunction = 
createUuidCastFunction(uuidTransformFunction, false);
+    testTransformFunctionMV(castTransformFunction, expectedValues);
+  }
+
+  private static CastTransformFunction 
createUuidCastFunction(TransformFunction uuidTransformFunction,
+      boolean nullHandlingEnabled) {
+    CastTransformFunction castTransformFunction = new CastTransformFunction();
+    LiteralTransformFunction targetType =
+        new LiteralTransformFunction(new 
LiteralContext(FieldSpec.DataType.STRING, "STRING"));
+    castTransformFunction.init(List.of(uuidTransformFunction, targetType), 
Map.of(), nullHandlingEnabled);
+    return castTransformFunction;
+  }
+
   @Test
   public void testCastTransformFunction() {
     ExpressionContext expression =


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

Reply via email to