Copilot commented on code in PR #17652:
URL: https://github.com/apache/pinot/pull/17652#discussion_r2775102945


##########
pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunction.java:
##########
@@ -98,7 +98,8 @@ public double[] transformToDoubleValuesSV(ValueBlock 
valueBlock) {
       }
     } else {
       for (int i = 0; i < length; i++) {
-        _doubleValuesSV[i] = Math.signum(leftValues[i]) * 
Math.floor(Math.abs(leftValues[i]));
+        // Keep behavior consistent with truncate(value, 0), including 
canonical +0.0 for values truncated to zero.
+        _doubleValuesSV[i] = BigDecimal.valueOf(leftValues[i]).setScale(0, 
RoundingMode.DOWN).doubleValue();

Review Comment:
   This changes behavior for non-finite doubles: 
`BigDecimal.valueOf(Double.NaN/Infinity)` throws `NumberFormatException`, 
whereas the previous `Math.signum/floor/abs` logic returned `NaN`/`Infinity`. 
If `truncate()` can be applied to columns containing non-finite values, this is 
a runtime regression. Consider preserving prior semantics by explicitly 
handling non-finite inputs (e.g., if not finite, return the original value) 
before using `BigDecimal`.



##########
pinot-controller/src/test/java/org/apache/pinot/controller/api/PinotTableRestletResourceTest.java:
##########
@@ -1197,18 +1197,21 @@ public void testTableTasksCleanupWithNonActiveTasks()
     String taskName = 
taskInfo.values().iterator().next().getScheduledTaskNames().get(0);
     waitForTaskState(taskName, TaskState.IN_PROGRESS);
 
-    // stop the task queue to abort the task
+    // Stop the task queue to abort the task. Keep it stopped until table 
deletion is complete to avoid
+    // task transition races while cleanup deletes Helix job metadata.
     sendPutRequest(DEFAULT_INSTANCE.getControllerRequestURLBuilder()
         
.forStopMinionTaskQueue(MinionConstants.SegmentGenerationAndPushTask.TASK_TYPE));
     waitForTaskState(taskName, TaskState.STOPPED);
-    // resume the task queue again to avoid affecting other tests
-    sendPutRequest(DEFAULT_INSTANCE.getControllerRequestURLBuilder()
-        
.forResumeMinionTaskQueue(MinionConstants.SegmentGenerationAndPushTask.TASK_TYPE));
-
-    // Delete table - should succeed and clean up tasks
-    String deleteResponse = sendDeleteRequest(
-        
DEFAULT_INSTANCE.getControllerRequestURLBuilder().forTableDelete(tableName));
-    assertEquals(deleteResponse, "{\"status\":\"Tables: [" + tableName + 
"_OFFLINE] deleted\"}");
+    try {
+      // Delete table - should succeed and clean up tasks
+      String deleteResponse = sendDeleteRequest(
+          
DEFAULT_INSTANCE.getControllerRequestURLBuilder().forTableDelete(tableName));
+      assertEquals(deleteResponse, "{\"status\":\"Tables: [" + tableName + 
"_OFFLINE] deleted\"}");
+    } finally {
+      // Resume queue to avoid affecting other tests.
+      sendPutRequest(DEFAULT_INSTANCE.getControllerRequestURLBuilder()
+          
.forResumeMinionTaskQueue(MinionConstants.SegmentGenerationAndPushTask.TASK_TYPE));
+    }

Review Comment:
   The `finally` only protects the table deletion section. If 
`waitForTaskState(taskName, TaskState.STOPPED)` fails/throws (e.g., timeout), 
the queue may remain stopped and leak state to other tests—the exact scenario 
the `finally` is meant to prevent. Consider widening the `try/finally` to cover 
everything after the stop request (including the STOPPED wait), so the resume 
always runs once a stop has been issued.



##########
pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunction.java:
##########
@@ -98,7 +98,8 @@ public double[] transformToDoubleValuesSV(ValueBlock 
valueBlock) {
       }
     } else {
       for (int i = 0; i < length; i++) {
-        _doubleValuesSV[i] = Math.signum(leftValues[i]) * 
Math.floor(Math.abs(leftValues[i]));
+        // Keep behavior consistent with truncate(value, 0), including 
canonical +0.0 for values truncated to zero.
+        _doubleValuesSV[i] = BigDecimal.valueOf(leftValues[i]).setScale(0, 
RoundingMode.DOWN).doubleValue();

Review Comment:
   Creating a `BigDecimal` per row in `transformToDoubleValuesSV()` can be 
expensive in hot query paths (allocations + parsing). Since the main goal is 
canonicalizing signed zero, consider a math-based truncation (toward zero) and 
then normalize zero explicitly (e.g., if truncated == 0.0d then return 0.0d) to 
avoid per-row `BigDecimal` overhead.
   ```suggestion
           double value = leftValues[i];
           double truncated;
           if (value > 0.0d) {
             truncated = Math.floor(value);
           } else if (value < 0.0d) {
             truncated = Math.ceil(value);
           } else {
             truncated = 0.0d;
           }
           // Normalize -0.0 to +0.0 to match BigDecimal behavior.
           if (truncated == 0.0d) {
             truncated = 0.0d;
           }
           _doubleValuesSV[i] = truncated;
   ```



##########
pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/TruncateDecimalTransformFunctionTest.java:
##########
@@ -62,6 +62,15 @@ public void testTruncateDecimalTransformFunction() {
       expectedValues[i] = truncate(_doubleSVValues[i], 0);
     }
     testTransformFunction(transformFunction, expectedValues);
+
+    // Regression for signed-zero handling: truncate(value) should match 
truncate(value, 0).
+    expression = RequestContextUtils.getExpression("truncate(-0.4)");
+    transformFunction = TransformFunctionFactory.get(expression, 
_dataSourceMap);
+    Assert.assertTrue(transformFunction instanceof 
TruncateDecimalTransformFunction);
+    for (int i = 0; i < NUM_ROWS; i++) {
+      expectedValues[i] = 0.0d;
+    }

Review Comment:
   This loop is filling the entire array with a constant value; using 
`Arrays.fill(expectedValues, 0.0d)` would be simpler and clearer, and avoids 
repeating the same fill-loop pattern in tests.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to