xiangfu0 commented on code in PR #19888:
URL: https://github.com/apache/pinot/pull/19888#discussion_r4288758904


##########
pinot-common/src/main/java/org/apache/pinot/common/evaluator/FunctionEvaluatorFactory.java:
##########
@@ -109,11 +111,40 @@ public static boolean isGroovyExpression(String 
transformExpression) {
     return 
transformExpression.startsWith(GroovyFunctionEvaluator.getGroovyExpressionPrefix());
   }
 
-  private static String getDefaultMapKeysTransformExpression(String 
mapColumnName) {
-    return String.format("Groovy({%s.sort()*.key}, %s)", mapColumnName, 
mapColumnName);
-  }
+  /// Extracts legacy Avro map keys or values in key order. Immutable and safe 
to share between threads.
+  private static class MapFunctionEvaluator implements FunctionEvaluator {
+    private final String _mapColumnName;
+    private final boolean _extractKeys;
+
+    MapFunctionEvaluator(String mapColumnName, boolean extractKeys) {
+      _mapColumnName = mapColumnName;
+      _extractKeys = extractKeys;
+    }
 
-  private static String getDefaultMapValuesTransformExpression(String 
mapColumnName) {
-    return String.format("Groovy({%s.sort()*.value}, %s)", mapColumnName, 
mapColumnName);
+    @Override
+    public List<String> getArguments() {
+      return List.of(_mapColumnName);
+    }
+
+    @Override
+    public Object evaluate(GenericRow genericRow) {
+      return evaluateMap(genericRow.getValue(_mapColumnName));
+    }
+
+    @Override
+    public Object evaluate(Object[] values) {
+      return evaluateMap(values[0]);
+    }
+
+    @Nullable
+    private List<?> evaluateMap(@Nullable Object value) {
+      if (value == null) {
+        return null;
+      }
+      Map<?, ?> map = (Map<?, ?>) value;
+      // Groovy's sort() preserves the comparator of an existing SortedMap.

Review Comment:
   This comment is misleading: Groovy's `Map.sort()` actually ignores any 
existing comparator and re-sorts into ascending natural key order, so the old 
Groovy path and this new code diverge when the input is a `SortedMap` with a 
non-natural comparator (old would force ascending, new keeps the reverse 
order). For plain Avro `HashMap`/`LinkedHashMap` inputs the two are equivalent, 
so real ingestion is unaffected, but please reword the comment to say the 
preservation is a deliberate new behavior rather than matching Groovy.



##########
pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/TableConfigValidationUtils.java:
##########
@@ -49,6 +51,23 @@ public final class TableConfigValidationUtils {
   private TableConfigValidationUtils() {
   }
 
+  /// Rejects legacy FieldSpec Groovy transforms before schema validation can 
compile them.
+  @SuppressWarnings("deprecation") // FieldSpec transforms remain supported in 
stored schemas.
+  static void validateSchemaGroovy(Schema schema, ControllerConf 
controllerConf) {
+    if (controllerConf.isDisableIngestionGroovy()) {
+      String groovyPrefix = 
GroovyFunctionEvaluator.getGroovyExpressionPrefix() + "(";
+      for (FieldSpec fieldSpec : schema.getAllFieldSpecs()) {
+        String transformFunction = fieldSpec.getTransformFunction();
+        if (transformFunction != null
+            && transformFunction.regionMatches(true, 0, groovyPrefix, 0, 
groovyPrefix.length())) {

Review Comment:
   Nice that this schema check matches the prefix case-insensitively, since the 
evaluator regex is case-insensitive. Just a heads-up that the table-level 
filter/transform checks in `TableConfigUtils` still use 
`FunctionEvaluatorFactory.isGroovyExpression`, which is case-sensitive 
(`startsWith("Groovy")`), so a lowercase `groovy({...})` slips past those. The 
new ingestion compiler gate backstops it, so this is only a defense-in-depth 
consistency note, not a blocker.



-- 
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