Browse Source

ESQL: Skip unsupported grapheme cluster test (#115258)

This skips the test for reversing grapheme clusters if the node doesn't
support reversing grapheme clusters. Nodes that are using a jdk before
20 won't support reversing grapheme clusters because they don't have
https://bugs.openjdk.org/browse/JDK-8292387

This reworks `EsqlCapabilities` so we can easilly register it only if
we're on jdk 20:
```
FN_REVERSE_GRAPHEME_CLUSTERS(Runtime.version().feature() < 20),
```

Closes #114537
Closes #114535
Closes #114536
Closes #114558
Closes #114559
Closes #114560
Nik Everett 11 months ago
parent
commit
f38f2301bc

+ 4 - 0
docs/reference/esql/functions/description/reverse.asciidoc

@@ -3,3 +3,7 @@
 *Description*
 
 Returns a new string representing the input string in reverse order.
+
+NOTE: If Elasticsearch is running with a JDK version less than 20 then this will not properly reverse Grapheme Clusters.
+Elastic Cloud the JDK bundled with Elasticsearch all use newer JDKs. But if you've explicitly shifted to an older jdk
+then you'll see things like "👍🏽😊" be reversed to  "🏽👍😊" instead of the correct "😊👍🏽".

+ 1 - 0
docs/reference/esql/functions/kibana/definition/reverse.json

@@ -3,6 +3,7 @@
   "type" : "eval",
   "name" : "reverse",
   "description" : "Returns a new string representing the input string in reverse order.",
+  "note" : "If Elasticsearch is running with a JDK version less than 20 then this will not properly reverse Grapheme Clusters.\nElastic Cloud the JDK bundled with Elasticsearch all use newer JDKs. But if you've explicitly shifted to an older jdk\nthen you'll see things like \"\uD83D\uDC4D\uD83C\uDFFD\uD83D\uDE0A\" be reversed to  \"\uD83C\uDFFD\uD83D\uDC4D\uD83D\uDE0A\" instead of the correct \"\uD83D\uDE0A\uD83D\uDC4D\uD83C\uDFFD\".",
   "signatures" : [
     {
       "params" : [

+ 3 - 0
docs/reference/esql/functions/kibana/docs/reverse.md

@@ -8,3 +8,6 @@ Returns a new string representing the input string in reverse order.
 ```
 ROW message = "Some Text" | EVAL message_reversed = REVERSE(message);
 ```
+Note: If Elasticsearch is running with a JDK version less than 20 then this will not properly reverse Grapheme Clusters.
+Elastic Cloud the JDK bundled with Elasticsearch all use newer JDKs. But if you've explicitly shifted to an older jdk
+then you'll see things like "👍🏽😊" be reversed to  "🏽👍😊" instead of the correct "😊👍🏽".

+ 1 - 0
x-pack/plugin/esql/qa/testFixtures/src/main/resources/string.csv-spec

@@ -1236,6 +1236,7 @@ off_on_holiday:keyword | back_home_again:keyword
 
 reverseGraphemeClusters
 required_capability: fn_reverse
+required_capability: fn_reverse_grapheme_clusters
 ROW message = "áéíóúàèìòùâêîôû😊👍🏽🎉💖कंठाी" | EVAL message_reversed = REVERSE(message);
 
 message:keyword                | message_reversed:keyword

+ 24 - 23
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

@@ -32,6 +32,12 @@ public class EsqlCapabilities {
          */
         FN_REVERSE,
 
+        /**
+         * Support for reversing whole grapheme clusters. This is not supported
+         * on JDK versions less than 20.
+         */
+        FN_REVERSE_GRAPHEME_CLUSTERS(Runtime.version().feature() < 20),
+
         /**
          * Support for function {@code CBRT}. Done in #108574.
          */
@@ -133,7 +139,7 @@ public class EsqlCapabilities {
          * - fixed variable shadowing
          * - fixed Join.references(), requiring breaking change to Join serialization
          */
-        LOOKUP_V4(true),
+        LOOKUP_V4(Build.current().isSnapshot()),
 
         /**
          * Support for requesting the "REPEAT" command.
@@ -279,7 +285,7 @@ public class EsqlCapabilities {
         /**
          * Support for match operator
          */
-        MATCH_OPERATOR(true),
+        MATCH_OPERATOR(Build.current().isSnapshot()),
 
         /**
          * Removing support for the {@code META} keyword.
@@ -349,7 +355,7 @@ public class EsqlCapabilities {
         /**
          * Supported the text categorization function "CATEGORIZE".
          */
-        CATEGORIZE(true),
+        CATEGORIZE(Build.current().isSnapshot()),
 
         /**
          * QSTR function
@@ -375,7 +381,7 @@ public class EsqlCapabilities {
         /**
          * Support named parameters for field names.
          */
-        NAMED_PARAMETER_FOR_FIELD_AND_FUNCTION_NAMES(true),
+        NAMED_PARAMETER_FOR_FIELD_AND_FUNCTION_NAMES(Build.current().isSnapshot()),
 
         /**
          * Fix sorting not allowed on _source and counters.
@@ -397,32 +403,22 @@ public class EsqlCapabilities {
          */
         FUNCTION_STATS;
 
-        private final boolean snapshotOnly;
-        private final FeatureFlag featureFlag;
+        private final boolean enabled;
 
         Cap() {
-            this(false, null);
+            this.enabled = true;
         };
 
-        Cap(boolean snapshotOnly) {
-            this(snapshotOnly, null);
+        Cap(boolean enabled) {
+            this.enabled = enabled;
         };
 
         Cap(FeatureFlag featureFlag) {
-            this(false, featureFlag);
-        }
-
-        Cap(boolean snapshotOnly, FeatureFlag featureFlag) {
-            assert featureFlag == null || snapshotOnly == false;
-            this.snapshotOnly = snapshotOnly;
-            this.featureFlag = featureFlag;
+            this.enabled = featureFlag.isEnabled();
         }
 
         public boolean isEnabled() {
-            if (featureFlag == null) {
-                return Build.current().isSnapshot() || this.snapshotOnly == false;
-            }
-            return featureFlag.isEnabled();
+            return enabled;
         }
 
         public String capabilityName() {
@@ -430,12 +426,17 @@ public class EsqlCapabilities {
         }
     }
 
-    public static final Set<String> CAPABILITIES = capabilities();
+    public static final Set<String> CAPABILITIES = capabilities(false);
 
-    private static Set<String> capabilities() {
+    /**
+     * Get a {@link Set} of all capabilities. If the {@code all} parameter is {@code false}
+     * then only <strong>enabled</strong> capabilities are returned - otherwise <strong>all</strong>
+     * known capabilities are returned.
+     */
+    public static Set<String> capabilities(boolean all) {
         List<String> caps = new ArrayList<>();
         for (Cap cap : Cap.values()) {
-            if (cap.isEnabled()) {
+            if (all || cap.isEnabled()) {
                 caps.add(cap.capabilityName());
             }
         }

+ 5 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Reverse.java

@@ -46,7 +46,11 @@ public class Reverse extends UnaryScalarFunction {
                 file = "string",
                 tag = "reverseEmoji",
                 description = "`REVERSE` works with unicode, too! It keeps unicode grapheme clusters together during reversal."
-            ) }
+            ) },
+        note = """
+            If Elasticsearch is running with a JDK version less than 20 then this will not properly reverse Grapheme Clusters.
+            Elastic Cloud and the JDK bundled with Elasticsearch all use newer JDKs. But if you've explicitly shifted to an older jdk
+            then you'll see things like "👍🏽😊" be reversed to  "🏽👍😊" instead of the correct "😊👍🏽"."""
     )
     public Reverse(
         Source source,

+ 1 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java

@@ -257,7 +257,7 @@ public class CsvTests extends ESTestCase {
                 assertThat(
                     "Capability is not included in the enabled list capabilities on a snapshot build. Spelling mistake?",
                     testCase.requiredCapabilities,
-                    everyItem(in(EsqlCapabilities.CAPABILITIES))
+                    everyItem(in(EsqlCapabilities.capabilities(true)))
                 );
             } else {
                 for (EsqlCapabilities.Cap c : EsqlCapabilities.Cap.values()) {