Browse Source

[8.x] [ESQL] Add support BYTE_LENGTH scalar function (#116591) (#116731)

Gal Lalouche 11 months ago
parent
commit
dae79b5c22
29 changed files with 669 additions and 160 deletions
  1. 5 0
      docs/changelog/116591.yaml
  2. 2 0
      docs/reference/esql/functions/description/bit_length.asciidoc
  3. 7 0
      docs/reference/esql/functions/description/byte_length.asciidoc
  4. 2 0
      docs/reference/esql/functions/description/length.asciidoc
  5. 13 0
      docs/reference/esql/functions/examples/byte_length.asciidoc
  6. 38 0
      docs/reference/esql/functions/kibana/definition/bit_length.json
  7. 38 0
      docs/reference/esql/functions/kibana/definition/byte_length.json
  8. 2 1
      docs/reference/esql/functions/kibana/definition/length.json
  9. 5 3
      docs/reference/esql/functions/kibana/docs/bit_length.md
  10. 14 0
      docs/reference/esql/functions/kibana/docs/byte_length.md
  11. 5 3
      docs/reference/esql/functions/kibana/docs/length.md
  12. 15 0
      docs/reference/esql/functions/layout/byte_length.asciidoc
  13. 6 0
      docs/reference/esql/functions/parameters/byte_length.asciidoc
  14. 1 0
      docs/reference/esql/functions/signature/byte_length.svg
  15. 2 0
      docs/reference/esql/functions/string-functions.asciidoc
  16. 10 0
      docs/reference/esql/functions/types/byte_length.asciidoc
  17. 30 29
      x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs.csv-spec
  18. 141 120
      x-pack/plugin/esql/qa/testFixtures/src/main/resources/eval.csv-spec
  19. 127 0
      x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ByteLengthEvaluator.java
  20. 5 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
  21. 2 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java
  22. 2 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/UnaryScalarFunction.java
  23. 1 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/BitLength.java
  24. 92 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ByteLength.java
  25. 1 0
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Length.java
  26. 19 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ByteLengthSerializationTests.java
  27. 77 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ByteLengthTests.java
  28. 2 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java
  29. 5 4
      x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/60_usage.yml

+ 5 - 0
docs/changelog/116591.yaml

@@ -0,0 +1,5 @@
+pr: 116591
+summary: "Add support for `BYTE_LENGTH` scalar function"
+area: ES|QL
+type: feature
+issues: []

+ 2 - 0
docs/reference/esql/functions/description/bit_length.asciidoc

@@ -3,3 +3,5 @@
 *Description*
 
 Returns the bit length of a string.
+
+NOTE: All strings are in UTF-8, so a single character can use multiple bytes.

+ 7 - 0
docs/reference/esql/functions/description/byte_length.asciidoc

@@ -0,0 +1,7 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Description*
+
+Returns the byte length of a string.
+
+NOTE: All strings are in UTF-8, so a single character can use multiple bytes.

+ 2 - 0
docs/reference/esql/functions/description/length.asciidoc

@@ -3,3 +3,5 @@
 *Description*
 
 Returns the character length of a string.
+
+NOTE: All strings are in UTF-8, so a single character can use multiple bytes.

+ 13 - 0
docs/reference/esql/functions/examples/byte_length.asciidoc

@@ -0,0 +1,13 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Example*
+
+[source.merge.styled,esql]
+----
+include::{esql-specs}/eval.csv-spec[tag=byteLength]
+----
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+include::{esql-specs}/eval.csv-spec[tag=byteLength-result]
+|===
+

+ 38 - 0
docs/reference/esql/functions/kibana/definition/bit_length.json

@@ -0,0 +1,38 @@
+{
+  "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+  "type" : "eval",
+  "name" : "bit_length",
+  "description" : "Returns the bit length of a string.",
+  "note" : "All strings are in UTF-8, so a single character can use multiple bytes.",
+  "signatures" : [
+    {
+      "params" : [
+        {
+          "name" : "string",
+          "type" : "keyword",
+          "optional" : false,
+          "description" : "String expression. If `null`, the function returns `null`."
+        }
+      ],
+      "variadic" : false,
+      "returnType" : "integer"
+    },
+    {
+      "params" : [
+        {
+          "name" : "string",
+          "type" : "text",
+          "optional" : false,
+          "description" : "String expression. If `null`, the function returns `null`."
+        }
+      ],
+      "variadic" : false,
+      "returnType" : "integer"
+    }
+  ],
+  "examples" : [
+    "FROM airports\n| WHERE country == \"India\"\n| KEEP city\n| EVAL fn_length=LENGTH(city), fn_bit_length = BIT_LENGTH(city)"
+  ],
+  "preview" : false,
+  "snapshot_only" : false
+}

+ 38 - 0
docs/reference/esql/functions/kibana/definition/byte_length.json

@@ -0,0 +1,38 @@
+{
+  "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+  "type" : "eval",
+  "name" : "byte_length",
+  "description" : "Returns the byte length of a string.",
+  "note" : "All strings are in UTF-8, so a single character can use multiple bytes.",
+  "signatures" : [
+    {
+      "params" : [
+        {
+          "name" : "string",
+          "type" : "keyword",
+          "optional" : false,
+          "description" : "String expression. If `null`, the function returns `null`."
+        }
+      ],
+      "variadic" : false,
+      "returnType" : "integer"
+    },
+    {
+      "params" : [
+        {
+          "name" : "string",
+          "type" : "text",
+          "optional" : false,
+          "description" : "String expression. If `null`, the function returns `null`."
+        }
+      ],
+      "variadic" : false,
+      "returnType" : "integer"
+    }
+  ],
+  "examples" : [
+    "FROM airports\n| WHERE country == \"India\"\n| KEEP city\n| EVAL fn_length=LENGTH(city), fn_byte_length = BYTE_LENGTH(city)"
+  ],
+  "preview" : false,
+  "snapshot_only" : false
+}

+ 2 - 1
docs/reference/esql/functions/kibana/definition/length.json

@@ -3,6 +3,7 @@
   "type" : "eval",
   "name" : "length",
   "description" : "Returns the character length of a string.",
+  "note" : "All strings are in UTF-8, so a single character can use multiple bytes.",
   "signatures" : [
     {
       "params" : [
@@ -30,7 +31,7 @@
     }
   ],
   "examples" : [
-    "FROM employees\n| KEEP first_name, last_name\n| EVAL fn_length = LENGTH(first_name)"
+    "FROM airports\n| WHERE country == \"India\"\n| KEEP city\n| EVAL fn_length = LENGTH(city)"
   ],
   "preview" : false,
   "snapshot_only" : false

+ 5 - 3
docs/reference/esql/functions/kibana/docs/bit_length.md

@@ -6,7 +6,9 @@ This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../READ
 Returns the bit length of a string.
 
 ```
-FROM employees
-| KEEP first_name, last_name
-| EVAL fn_bit_length = BIT_LENGTH(first_name)
+FROM airports
+| WHERE country == "India"
+| KEEP city
+| EVAL fn_length=LENGTH(city), fn_bit_length = BIT_LENGTH(city)
 ```
+Note: All strings are in UTF-8, so a single character can use multiple bytes.

+ 14 - 0
docs/reference/esql/functions/kibana/docs/byte_length.md

@@ -0,0 +1,14 @@
+<!--
+This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+-->
+
+### BYTE_LENGTH
+Returns the byte length of a string.
+
+```
+FROM airports
+| WHERE country == "India"
+| KEEP city
+| EVAL fn_length=LENGTH(city), fn_byte_length = BYTE_LENGTH(city)
+```
+Note: All strings are in UTF-8, so a single character can use multiple bytes.

+ 5 - 3
docs/reference/esql/functions/kibana/docs/length.md

@@ -6,7 +6,9 @@ This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../READ
 Returns the character length of a string.
 
 ```
-FROM employees
-| KEEP first_name, last_name
-| EVAL fn_length = LENGTH(first_name)
+FROM airports
+| WHERE country == "India"
+| KEEP city
+| EVAL fn_length = LENGTH(city)
 ```
+Note: All strings are in UTF-8, so a single character can use multiple bytes.

+ 15 - 0
docs/reference/esql/functions/layout/byte_length.asciidoc

@@ -0,0 +1,15 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+[discrete]
+[[esql-byte_length]]
+=== `BYTE_LENGTH`
+
+*Syntax*
+
+[.text-center]
+image::esql/functions/signature/byte_length.svg[Embedded,opts=inline]
+
+include::../parameters/byte_length.asciidoc[]
+include::../description/byte_length.asciidoc[]
+include::../types/byte_length.asciidoc[]
+include::../examples/byte_length.asciidoc[]

+ 6 - 0
docs/reference/esql/functions/parameters/byte_length.asciidoc

@@ -0,0 +1,6 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Parameters*
+
+`string`::
+String expression. If `null`, the function returns `null`.

+ 1 - 0
docs/reference/esql/functions/signature/byte_length.svg

@@ -0,0 +1 @@
+<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="348" height="46" viewbox="0 0 348 46"><defs><style type="text/css">#guide .c{fill:none;stroke:#222222;}#guide .k{fill:#000000;font-family:Roboto Mono,Sans-serif;font-size:20px;}#guide .s{fill:#e4f4ff;stroke:#222222;}#guide .syn{fill:#8D8D8D;font-family:Roboto Mono,Sans-serif;font-size:20px;}</style></defs><path class="c" d="M0 31h5m152 0h10m32 0h10m92 0h10m32 0h5"/><rect class="s" x="5" y="5" width="152" height="36"/><text class="k" x="15" y="31">BYTE_LENGTH</text><rect class="s" x="167" y="5" width="32" height="36" rx="7"/><text class="syn" x="177" y="31">(</text><rect class="s" x="209" y="5" width="92" height="36" rx="7"/><text class="k" x="219" y="31">string</text><rect class="s" x="311" y="5" width="32" height="36" rx="7"/><text class="syn" x="321" y="31">)</text></svg>

+ 2 - 0
docs/reference/esql/functions/string-functions.asciidoc

@@ -9,6 +9,7 @@
 
 // tag::string_list[]
 * <<esql-bit_length>>
+* <<esql-byte_length>>
 * <<esql-concat>>
 * <<esql-ends_with>>
 * <<esql-from_base64>>
@@ -32,6 +33,7 @@
 // end::string_list[]
 
 include::layout/bit_length.asciidoc[]
+include::layout/byte_length.asciidoc[]
 include::layout/concat.asciidoc[]
 include::layout/ends_with.asciidoc[]
 include::layout/from_base64.asciidoc[]

+ 10 - 0
docs/reference/esql/functions/types/byte_length.asciidoc

@@ -0,0 +1,10 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Supported types*
+
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+string | result
+keyword | integer
+text | integer
+|===

+ 30 - 29
x-pack/plugin/esql/qa/testFixtures/src/main/resources/docs.csv-spec

@@ -4,7 +4,7 @@
 // the comments in whatever file the test already lives in. If you have to
 // write a new test to make an example in the docs then put it in whatever
 // file matches its "theme" best. Put it next to similar tests. Not here.
- 
+
 // Also! When Nik originally extracted examples from the docs to make them
 // testable he didn't spend a lot of time putting the docs into appropriate
 // files. He just made this one. He didn't put his toys away. We'd be better
@@ -352,18 +352,18 @@ FROM employees
 
 // tag::case-result[]
 emp_no:integer | languages:integer| type:keyword
-10001          | 2                |bilingual      
-10002          | 5                |polyglot       
-10003          | 4                |polyglot       
-10004          | 5                |polyglot       
-10005          | 1                |monolingual    
+10001          | 2                |bilingual
+10002          | 5                |polyglot
+10003          | 4                |polyglot
+10004          | 5                |polyglot
+10005          | 1                |monolingual
 // end::case-result[]
 ;
 
 docsCountAll
 // tag::countAll[]
-FROM employees 
-| STATS count = COUNT(*) BY languages 
+FROM employees
+| STATS count = COUNT(*) BY languages
 | SORT languages DESC
 // end::countAll[]
 ;
@@ -371,7 +371,7 @@ FROM employees
 // tag::countAll-result[]
 count:long | languages:integer
 10         |null
-21         |5 
+21         |5
 18         |4
 17         |3
 19         |2
@@ -381,8 +381,8 @@ count:long | languages:integer
 
 basicGrok
 // tag::basicGrok[]
-ROW a = "2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42" 
-| GROK a """%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num}""" 
+ROW a = "2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42"
+| GROK a """%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num}"""
 | KEEP date, ip, email, num
 // end::basicGrok[]
 ;
@@ -395,8 +395,8 @@ date:keyword          | ip:keyword    | email:keyword       | num:keyword
 
 grokWithConversionSuffix
 // tag::grokWithConversionSuffix[]
-ROW a = "2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42" 
-| GROK a """%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num:int}""" 
+ROW a = "2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42"
+| GROK a """%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num:int}"""
 | KEEP date, ip, email, num
 // end::grokWithConversionSuffix[]
 ;
@@ -409,8 +409,8 @@ date:keyword              | ip:keyword    | email:keyword       | num:integer
 
 grokWithToDatetime
 // tag::grokWithToDatetime[]
-ROW a = "2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42" 
-| GROK a """%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num:int}""" 
+ROW a = "2023-01-23T12:15:00.000Z 127.0.0.1 some.email@foo.com 42"
+| GROK a """%{TIMESTAMP_ISO8601:date} %{IP:ip} %{EMAILADDRESS:email} %{NUMBER:num:int}"""
 | KEEP date, ip, email, num
 | EVAL date = TO_DATETIME(date)
 // end::grokWithToDatetime[]
@@ -471,7 +471,7 @@ Tokyo             | 100-7014         | null
 
 basicDissect
 // tag::basicDissect[]
-ROW a = "2023-01-23T12:15:00.000Z - some text - 127.0.0.1" 
+ROW a = "2023-01-23T12:15:00.000Z - some text - 127.0.0.1"
 | DISSECT a """%{date} - %{msg} - %{ip}"""
 | KEEP date, msg, ip
 // end::basicDissect[]
@@ -485,8 +485,8 @@ date:keyword             | msg:keyword  | ip:keyword
 
 dissectWithToDatetime
 // tag::dissectWithToDatetime[]
-ROW a = "2023-01-23T12:15:00.000Z - some text - 127.0.0.1" 
-| DISSECT a """%{date} - %{msg} - %{ip}""" 
+ROW a = "2023-01-23T12:15:00.000Z - some text - 127.0.0.1"
+| DISSECT a """%{date} - %{msg} - %{ip}"""
 | KEEP date, msg, ip
 | EVAL date = TO_DATETIME(date)
 // end::dissectWithToDatetime[]
@@ -574,8 +574,8 @@ FROM employees
 
 // tag::like-result[]
 first_name:keyword  | last_name:keyword
-Ebbe           |Callaway    
-Eberhardt      |Terkki         
+Ebbe           |Callaway
+Eberhardt      |Terkki
 // end::like-result[]
 ;
 
@@ -589,7 +589,7 @@ FROM employees
 
 // tag::rlike-result[]
 first_name:keyword  | last_name:keyword
-Alejandro      |McAlpine     
+Alejandro      |McAlpine
 // end::rlike-result[]
 ;
 
@@ -660,18 +660,19 @@ FROM sample_data
 docsBitLength
 required_capability: fn_bit_length
 // tag::bitLength[]
-FROM employees
-| KEEP first_name, last_name
-| EVAL fn_bit_length = BIT_LENGTH(first_name)
+FROM airports
+| WHERE country == "India"
+| KEEP city
+| EVAL fn_length=LENGTH(city), fn_bit_length = BIT_LENGTH(city)
 // end::bitLength[]
-| SORT first_name
+| SORT city
 | LIMIT 3
 ;
 
 // tag::bitLength-result[]
-first_name:keyword   | last_name:keyword   | fn_bit_length:integer
-Alejandro      |McAlpine       |72              
-Amabile        |Gomatam        |56              
-Anneke         |Preusig        |48
+city:keyword  | fn_length:integer | fn_bit_length:integer
+Agwār         | 5                 | 48
+Ahmedabad     | 9                 | 72
+Bangalore     | 9                 | 72
 // end::bitLength-result[]
 ;

+ 141 - 120
x-pack/plugin/esql/qa/testFixtures/src/main/resources/eval.csv-spec

@@ -22,7 +22,7 @@ FROM addresses
 | SORT city.name
 ;
 
-city.country.name:keyword | city.name:keyword | city.country.continent.planet.name:keyword 
+city.country.name:keyword | city.name:keyword | city.country.continent.planet.name:keyword
 Netherlands               | Amsterdam         | EARTH
 United States of America  | San Francisco     | EARTH
 Japan                     | Tokyo             | EARTH
@@ -138,39 +138,39 @@ a:integer | b:integer | c:integer | d:integer | e:integer
 multipleDuplicateInterleaved1
 row a = 1 | eval b = a, c = 1, c = 3, d = b + 1, b = c * 2, c = 2, c = d * c + b | keep a, b, c, d;
 
-a:integer | b:integer | c:integer | d:integer 
-1         | 6         | 10        | 2         
+a:integer | b:integer | c:integer | d:integer
+1         | 6         | 10        | 2
 ;
 
 
 multipleDuplicateInterleaved2
 row a = 1 | eval b = a, c = 1 | eval c = 3, d = b + 1 | eval b = c * 2, c = 2 | eval c = d * c + b | keep a, b, c, d;
 
-a:integer | b:integer | c:integer | d:integer 
-1         | 6         | 10        | 2         
+a:integer | b:integer | c:integer | d:integer
+1         | 6         | 10        | 2
 ;
 
 
 multipleDuplicateInterleaved3
 row a = 1 | eval b = a, c = 1, c = 3 | eval d = b + 1 | eval b = c * 2, c = 2, c = d * c + b | keep a, b, c, d;
 
-a:integer | b:integer | c:integer | d:integer 
-1         | 6         | 10        | 2         
+a:integer | b:integer | c:integer | d:integer
+1         | 6         | 10        | 2
 ;
 
 multipleDuplicateInterleaved4
 row a = 1 | eval b = a | eval c = 1 | eval c = 3 | eval d = b + 1 | eval b = c * 2 | eval c = 2 | eval c = d * c + b | keep a, b, c, d;
 
-a:integer | b:integer | c:integer | d:integer 
-1         | 6         | 10         | 2         
+a:integer | b:integer | c:integer | d:integer
+1         | 6         | 10         | 2
 ;
 
 
 projectEval
 row x = 1  | keep x | eval a1 = x + 1, a2 = x + 1, a3 = a1 + a2, a1 = a1 + a2;
 
-x:integer | a2:integer | a3:integer | a1:integer 
-1         | 2          | 4          | 4         
+x:integer | a2:integer | a3:integer | a1:integer
+1         | 2          | 4          | 4
 ;
 
 evalNullSort
@@ -195,76 +195,76 @@ Uri
 evalWithIsNullIsNotNull
 from employees | eval true_bool = null is null, false_bool = null is not null, negated_true = not(null is null), negated_false = not(null is not null) | sort emp_no | limit 1 | keep *true*, *false*, first_name, last_name;
 
-true_bool:boolean  | negated_true:boolean | false_bool:boolean | negated_false:boolean | first_name:keyword | last_name:keyword   
+true_bool:boolean  | negated_true:boolean | false_bool:boolean | negated_false:boolean | first_name:keyword | last_name:keyword
 true               | false                | false              | true                  | Georgi             | Facello
 ;
 
 
 repetitiveEval
-from employees | sort emp_no | keep emp_no | eval sum = emp_no + 1 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no  
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no 
-| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
-| limit 3 
+from employees | sort emp_no | keep emp_no | eval sum = emp_no + 1
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no | eval sum = sum + emp_no
+| limit 3
 ;
 
 emp_no:i | sum:i
 10001 | 3230324
 10002 | 3230647
 10003 | 3230970
-; 
+;
 
 chainedEvalReusingPreviousValue
 from employees | sort emp_no | eval x1 = concat(first_name, "."), x2 = concat(x1, "."), x3 = concat(x2, ".") | keep x*, first_name | limit 5;
 
-   x1:keyword  |   x2:keyword  |  x3:keyword   |first_name:keyword   
-Georgi.        |Georgi..       |Georgi...      |Georgi         
-Bezalel.       |Bezalel..      |Bezalel...     |Bezalel        
-Parto.         |Parto..        |Parto...       |Parto          
-Chirstian.     |Chirstian..    |Chirstian...   |Chirstian      
+   x1:keyword  |   x2:keyword  |  x3:keyword   |first_name:keyword
+Georgi.        |Georgi..       |Georgi...      |Georgi
+Bezalel.       |Bezalel..      |Bezalel...     |Bezalel
+Parto.         |Parto..        |Parto...       |Parto
+Chirstian.     |Chirstian..    |Chirstian...   |Chirstian
 Kyoichi.       |Kyoichi..      |Kyoichi...     |Kyoichi
 ;
 
@@ -272,10 +272,10 @@ chainedEvalReusingPreviousValue2
 from employees | sort emp_no | eval x1 = concat(first_name, "."), x2 = concat(x1, last_name), x3 = concat(x2, gender) | keep x*, first_name, gender | limit 5;
 
    x1:keyword  |   x2:keyword    |  x3:keyword      |first_name:keyword|gender:keyword
-Georgi.        |Georgi.Facello   |Georgi.FacelloM   |Georgi            |M              
-Bezalel.       |Bezalel.Simmel   |Bezalel.SimmelF   |Bezalel           |F              
-Parto.         |Parto.Bamford    |Parto.BamfordM    |Parto             |M              
-Chirstian.     |Chirstian.Koblick|Chirstian.KoblickM|Chirstian         |M              
+Georgi.        |Georgi.Facello   |Georgi.FacelloM   |Georgi            |M
+Bezalel.       |Bezalel.Simmel   |Bezalel.SimmelF   |Bezalel           |F
+Parto.         |Parto.Bamford    |Parto.BamfordM    |Parto             |M
+Chirstian.     |Chirstian.Koblick|Chirstian.KoblickM|Chirstian         |M
 Kyoichi.       |Kyoichi.Maliniak |Kyoichi.MaliniakM |Kyoichi           |M
 ;
 
@@ -283,10 +283,10 @@ chainedEvalReusingPreviousValue3
 from employees | sort emp_no | eval x1 = concat(first_name, "."), x2 = concat(x1, last_name), x3 = concat(x2, x1) | keep x*, first_name | limit 5;
 
    x1:keyword  |   x2:keyword    |        x3:keyword         |first_name:keyword
-Georgi.        |Georgi.Facello   |Georgi.FacelloGeorgi.      |Georgi         
-Bezalel.       |Bezalel.Simmel   |Bezalel.SimmelBezalel.     |Bezalel        
-Parto.         |Parto.Bamford    |Parto.BamfordParto.        |Parto          
-Chirstian.     |Chirstian.Koblick|Chirstian.KoblickChirstian.|Chirstian      
+Georgi.        |Georgi.Facello   |Georgi.FacelloGeorgi.      |Georgi
+Bezalel.       |Bezalel.Simmel   |Bezalel.SimmelBezalel.     |Bezalel
+Parto.         |Parto.Bamford    |Parto.BamfordParto.        |Parto
+Chirstian.     |Chirstian.Koblick|Chirstian.KoblickChirstian.|Chirstian
 Kyoichi.       |Kyoichi.Maliniak |Kyoichi.MaliniakKyoichi.   |Kyoichi
 ;
 
@@ -301,7 +301,7 @@ warning:Line 1:88: java.lang.IllegalArgumentException: single-value function enc
 warning:Line 1:133: evaluation of [round([1.14], [1, 2])] failed, treating result as null. Only first 20 failures recorded.
 warning:Line 1:133: java.lang.IllegalArgumentException: single-value function encountered multi-value
 
-a:double | b:double   | c:double | d: double | e:double  | f:double  | g:double  | h:double 
+a:double | b:double   | c:double | d: double | e:double  | f:double  | g:double  | h:double
 1.2      | [2.4, 7.9] | 1.0      | null      | 1.0       | null      | 1.1       | null
 ;
 
@@ -356,22 +356,43 @@ FROM sample_data
 
 docsLength
 // tag::length[]
-FROM employees
-| KEEP first_name, last_name
-| EVAL fn_length = LENGTH(first_name)
+FROM airports
+| WHERE country == "India"
+| KEEP city
+| EVAL fn_length = LENGTH(city)
 // end::length[]
-| SORT first_name
+| SORT city
 | LIMIT 3
 ;
 
 // tag::length-result[]
-first_name:keyword   | last_name:keyword   | fn_length:integer
-Alejandro      |McAlpine       |9              
-Amabile        |Gomatam        |7              
-Anneke         |Preusig        |6
+city:keyword  | fn_length:integer
+Agwār         | 5
+Ahmedabad     | 9
+Bangalore     | 9
 // end::length-result[]
 ;
 
+docsByteLength
+required_capability: fn_byte_length
+// tag::byteLength[]
+FROM airports
+| WHERE country == "India"
+| KEEP city
+| EVAL fn_length=LENGTH(city), fn_byte_length = BYTE_LENGTH(city)
+// end::byteLength[]
+| SORT city
+| LIMIT 3
+;
+
+// tag::byteLength-result[]
+city:keyword  | fn_length:integer | fn_byte_length:integer
+Agwār         | 5                 | 6
+Ahmedabad     | 9                 | 9
+Bangalore     | 9                 | 9
+// end::byteLength-result[]
+;
+
 docsGettingStartedEvalNoColumnName
 // tag::gs-eval-no-column-name[]
 FROM sample_data
@@ -407,8 +428,8 @@ FROM employees
 // tag::eval-result[]
 first_name:keyword | last_name:keyword | height:double | height_feet:double | height_cm:double
 Georgi         |Facello        |2.03           |6.66043           |202.99999999999997
-Bezalel        |Simmel         |2.08           |6.82448           |208.0             
-Parto          |Bamford        |1.83           |6.004230000000001 |183.0  
+Bezalel        |Simmel         |2.08           |6.82448           |208.0
+Parto          |Bamford        |1.83           |6.004230000000001 |183.0
 // end::eval-result[]
 ;
 
@@ -423,9 +444,9 @@ FROM employees
 
 // tag::evalReplace-result[]
 first_name:keyword | last_name:keyword | height:double
-Georgi         |Facello        |6.66043           
-Bezalel        |Simmel         |6.82448           
-Parto          |Bamford        |6.004230000000001 
+Georgi         |Facello        |6.66043
+Bezalel        |Simmel         |6.82448
+Parto          |Bamford        |6.004230000000001
 // end::evalReplace-result[]
 ;
 
@@ -440,8 +461,8 @@ FROM employees
 
 // tag::evalUnnamedColumn-result[]
 first_name:keyword | last_name:keyword | height:double | height * 3.281:double
-Georgi         |Facello        |2.03           |6.66043           
-Bezalel        |Simmel         |2.08           |6.82448           
+Georgi         |Facello        |2.03           |6.66043
+Bezalel        |Simmel         |2.08           |6.82448
 Parto          |Bamford        |1.83           |6.004230000000001
 // end::evalUnnamedColumn-result[]
 ;
@@ -524,16 +545,16 @@ FROM employees
 | KEEP emp_no, salary, sum
 ;
 
-  emp_no:i     |   salary:i    |      sum:i      
--10015         |25324          |35339          
--10035         |25945          |35980          
--10092         |25976          |36068          
--10048         |26436          |36484          
--10057         |27215          |37272          
--10084         |28035          |38119          
--10026         |28336          |38362          
--10068         |28941          |39009          
--10060         |29175          |39235          
+  emp_no:i     |   salary:i    |      sum:i
+-10015         |25324          |35339
+-10035         |25945          |35980
+-10092         |25976          |36068
+-10048         |26436          |36484
+-10057         |27215          |37272
+-10084         |28035          |38119
+-10026         |28336          |38362
+-10068         |28941          |39009
+-10060         |29175          |39235
 -10042         |30404          |40446
 ;
 
@@ -545,16 +566,16 @@ from employees
 | limit 10
 ;
 
- first_name:keyword | last_name:keyword | salary:integer|ll:keyword|lf:keyword     
-Mona                |Azuma              |46595          |A         |M             
-Satosi              |Awdeh              |50249          |A         |S             
-Brendon             |Bernini            |33370          |B         |B             
-Breannda            |Billingsley        |29175          |B         |B             
-Cristinel           |Bouloucos          |58715          |B         |C             
-Charlene            |Brattka            |28941          |B         |C             
-Margareta           |Bierman            |41933          |B         |M             
-Mokhtar             |Bernatsky          |38992          |B         |M             
-Parto               |Bamford            |61805          |B         |P             
+ first_name:keyword | last_name:keyword | salary:integer|ll:keyword|lf:keyword
+Mona                |Azuma              |46595          |A         |M
+Satosi              |Awdeh              |50249          |A         |S
+Brendon             |Bernini            |33370          |B         |B
+Breannda            |Billingsley        |29175          |B         |B
+Cristinel           |Bouloucos          |58715          |B         |C
+Charlene            |Brattka            |28941          |B         |C
+Margareta           |Bierman            |41933          |B         |M
+Mokhtar             |Bernatsky          |38992          |B         |M
+Parto               |Bamford            |61805          |B         |P
 Premal              |Baek               |52833          |B         |P
 ;
 
@@ -568,15 +589,15 @@ from employees
 | limit 10
 ;
 
-     fn:keyword     |     ln:keyword    | salary:integer|       c:keyword     
-Mona                |Azuma              |46595          |AM             
-Satosi              |Awdeh              |50249          |AS             
-Brendon             |Bernini            |33370          |BB             
-Breannda            |Billingsley        |29175          |BB             
-Cristinel           |Bouloucos          |58715          |BC             
-Charlene            |Brattka            |28941          |BC             
-Margareta           |Bierman            |41933          |BM             
-Mokhtar             |Bernatsky          |38992          |BM             
-Parto               |Bamford            |61805          |BP             
+     fn:keyword     |     ln:keyword    | salary:integer|       c:keyword
+Mona                |Azuma              |46595          |AM
+Satosi              |Awdeh              |50249          |AS
+Brendon             |Bernini            |33370          |BB
+Breannda            |Billingsley        |29175          |BB
+Cristinel           |Bouloucos          |58715          |BC
+Charlene            |Brattka            |28941          |BC
+Margareta           |Bierman            |41933          |BM
+Mokhtar             |Bernatsky          |38992          |BM
+Parto               |Bamford            |61805          |BP
 Premal              |Baek               |52833          |BP
 ;

+ 127 - 0
x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/string/ByteLengthEvaluator.java

@@ -0,0 +1,127 @@
+// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+// or more contributor license agreements. Licensed under the Elastic License
+// 2.0; you may not use this file except in compliance with the Elastic License
+// 2.0.
+package org.elasticsearch.xpack.esql.expression.function.scalar.string;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.Page;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.compute.operator.Warnings;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ByteLength}.
+ * This class is generated. Do not edit it.
+ */
+public final class ByteLengthEvaluator implements EvalOperator.ExpressionEvaluator {
+  private final Source source;
+
+  private final EvalOperator.ExpressionEvaluator val;
+
+  private final DriverContext driverContext;
+
+  private Warnings warnings;
+
+  public ByteLengthEvaluator(Source source, EvalOperator.ExpressionEvaluator val,
+      DriverContext driverContext) {
+    this.source = source;
+    this.val = val;
+    this.driverContext = driverContext;
+  }
+
+  @Override
+  public Block eval(Page page) {
+    try (BytesRefBlock valBlock = (BytesRefBlock) val.eval(page)) {
+      BytesRefVector valVector = valBlock.asVector();
+      if (valVector == null) {
+        return eval(page.getPositionCount(), valBlock);
+      }
+      return eval(page.getPositionCount(), valVector).asBlock();
+    }
+  }
+
+  public IntBlock eval(int positionCount, BytesRefBlock valBlock) {
+    try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) {
+      BytesRef valScratch = new BytesRef();
+      position: for (int p = 0; p < positionCount; p++) {
+        if (valBlock.isNull(p)) {
+          result.appendNull();
+          continue position;
+        }
+        if (valBlock.getValueCount(p) != 1) {
+          if (valBlock.getValueCount(p) > 1) {
+            warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+          }
+          result.appendNull();
+          continue position;
+        }
+        result.appendInt(ByteLength.process(valBlock.getBytesRef(valBlock.getFirstValueIndex(p), valScratch)));
+      }
+      return result.build();
+    }
+  }
+
+  public IntVector eval(int positionCount, BytesRefVector valVector) {
+    try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) {
+      BytesRef valScratch = new BytesRef();
+      position: for (int p = 0; p < positionCount; p++) {
+        result.appendInt(p, ByteLength.process(valVector.getBytesRef(p, valScratch)));
+      }
+      return result.build();
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "ByteLengthEvaluator[" + "val=" + val + "]";
+  }
+
+  @Override
+  public void close() {
+    Releasables.closeExpectNoException(val);
+  }
+
+  private Warnings warnings() {
+    if (warnings == null) {
+      this.warnings = Warnings.createWarnings(
+              driverContext.warningsMode(),
+              source.source().getLineNumber(),
+              source.source().getColumnNumber(),
+              source.text()
+          );
+    }
+    return warnings;
+  }
+
+  static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+    private final Source source;
+
+    private final EvalOperator.ExpressionEvaluator.Factory val;
+
+    public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory val) {
+      this.source = source;
+      this.val = val;
+    }
+
+    @Override
+    public ByteLengthEvaluator get(DriverContext context) {
+      return new ByteLengthEvaluator(source, val.get(context), context);
+    }
+
+    @Override
+    public String toString() {
+      return "ByteLengthEvaluator[" + "val=" + val + "]";
+    }
+  }
+}

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

@@ -33,6 +33,11 @@ public class EsqlCapabilities {
          */
         FN_BIT_LENGTH,
 
+        /**
+         * Support for function {@code BYTE_LENGTH}.
+         */
+        FN_BYTE_LENGTH,
+
         /**
          * Support for function {@code REVERSE}.
          */

+ 2 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java

@@ -118,6 +118,7 @@ import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StDistanc
 import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StX;
 import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StY;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.BitLength;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.ByteLength;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.Concat;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.EndsWith;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.LTrim;
@@ -311,6 +312,7 @@ public class EsqlFunctionRegistry {
             // string
             new FunctionDefinition[] {
                 def(BitLength.class, BitLength::new, "bit_length"),
+                def(ByteLength.class, ByteLength::new, "byte_length"),
                 def(Concat.class, Concat::new, "concat"),
                 def(EndsWith.class, EndsWith::new, "ends_with"),
                 def(LTrim.class, LTrim::new, "ltrim"),

+ 2 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/UnaryScalarFunction.java

@@ -55,6 +55,7 @@ import org.elasticsearch.xpack.esql.expression.function.scalar.math.Tanh;
 import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.AbstractMultivalueFunction;
 import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StX;
 import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StY;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.ByteLength;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.LTrim;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.Length;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.RLike;
@@ -80,6 +81,7 @@ public abstract class UnaryScalarFunction extends EsqlScalarFunction {
         entries.add(Acos.ENTRY);
         entries.add(Asin.ENTRY);
         entries.add(Atan.ENTRY);
+        entries.add(ByteLength.ENTRY);
         entries.add(Cbrt.ENTRY);
         entries.add(Ceil.ENTRY);
         entries.add(Cos.ENTRY);

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

@@ -40,6 +40,7 @@ public class BitLength extends UnaryScalarFunction {
     @FunctionInfo(
         returnType = "integer",
         description = "Returns the bit length of a string.",
+        note = "All strings are in UTF-8, so a single character can use multiple bytes.",
         examples = @Example(file = "docs", tag = "bitLength")
     )
     public BitLength(

+ 92 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ByteLength.java

@@ -0,0 +1,92 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.expression.function.scalar.string;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.Evaluator;
+import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.expression.function.Example;
+import org.elasticsearch.xpack.esql.expression.function.FunctionInfo;
+import org.elasticsearch.xpack.esql.expression.function.Param;
+import org.elasticsearch.xpack.esql.expression.function.scalar.UnaryScalarFunction;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT;
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString;
+
+public class ByteLength extends UnaryScalarFunction {
+    public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+        Expression.class,
+        "ByteLength",
+        ByteLength::new
+    );
+
+    @FunctionInfo(
+        returnType = "integer",
+        description = "Returns the byte length of a string.",
+        note = "All strings are in UTF-8, so a single character can use multiple bytes.",
+        examples = @Example(file = "eval", tag = "byteLength")
+    )
+    public ByteLength(
+        Source source,
+        @Param(
+            name = "string",
+            type = { "keyword", "text" },
+            description = "String expression. If `null`, the function returns `null`."
+        ) Expression field
+    ) {
+        super(source, field);
+    }
+
+    private ByteLength(StreamInput in) throws IOException {
+        super(in);
+    }
+
+    @Override
+    public String getWriteableName() {
+        return ENTRY.name;
+    }
+
+    @Override
+    public DataType dataType() {
+        return DataType.INTEGER;
+    }
+
+    @Override
+    protected TypeResolution resolveType() {
+        return childrenResolved() ? isString(field(), sourceText(), DEFAULT) : new TypeResolution("Unresolved children");
+    }
+
+    @Evaluator
+    static int process(BytesRef val) {
+        return val.length;
+    }
+
+    @Override
+    public Expression replaceChildren(List<Expression> newChildren) {
+        return new ByteLength(source(), newChildren.get(0));
+    }
+
+    @Override
+    protected NodeInfo<? extends Expression> info() {
+        return NodeInfo.create(this, ByteLength::new, field());
+    }
+
+    @Override
+    public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
+        return new ByteLengthEvaluator.Factory(source(), toEvaluator.apply(field()));
+    }
+}

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

@@ -34,6 +34,7 @@ public class Length extends UnaryScalarFunction {
     @FunctionInfo(
         returnType = "integer",
         description = "Returns the character length of a string.",
+        note = "All strings are in UTF-8, so a single character can use multiple bytes.",
         examples = @Example(file = "eval", tag = "length")
     )
     public Length(

+ 19 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ByteLengthSerializationTests.java

@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.expression.function.scalar.string;
+
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.AbstractUnaryScalarSerializationTests;
+
+public class ByteLengthSerializationTests extends AbstractUnaryScalarSerializationTests<ByteLength> {
+    @Override
+    protected ByteLength create(Source source, Expression child) {
+        return new ByteLength(source, child);
+    }
+}

+ 77 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ByteLengthTests.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.expression.function.scalar.string;
+
+import com.carrotsearch.randomizedtesting.annotations.Name;
+import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase;
+import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import static org.hamcrest.Matchers.equalTo;
+
+public class ByteLengthTests extends AbstractScalarFunctionTestCase {
+    public ByteLengthTests(@Name("TestCase") Supplier<TestCaseSupplier.TestCase> testCaseSupplier) {
+        this.testCase = testCaseSupplier.get();
+    }
+
+    @ParametersFactory
+    public static Iterable<Object[]> parameters() {
+        List<TestCaseSupplier> cases = new ArrayList<>();
+        cases.addAll(List.of(new TestCaseSupplier("byte length basic test", List.of(DataType.KEYWORD), () -> {
+            var s = randomAlphaOfLength(between(0, 10000));
+            return testCase(s, DataType.KEYWORD, s.length());
+        })));
+        cases.addAll(makeTestCases("empty string", () -> "", 0));
+        cases.addAll(makeTestCases("single ascii character", () -> "a", 1));
+        cases.addAll(makeTestCases("ascii string", () -> "clump", 5));
+        cases.addAll(makeTestCases("3 bytes, 1 code point", () -> "☕", 3));
+        cases.addAll(makeTestCases("6 bytes, 2 code points", () -> "❗️", 6));
+        cases.addAll(makeTestCases("100 random alpha", () -> randomAlphaOfLength(100), 100));
+        return parameterSuppliersFromTypedDataWithDefaultChecks(ENTIRELY_NULL_PRESERVES_TYPE, cases, (v, p) -> "string");
+    }
+
+    private static List<TestCaseSupplier> makeTestCases(String title, Supplier<String> text, int expectedByteLength) {
+        return Stream.of(DataType.KEYWORD, DataType.TEXT, DataType.SEMANTIC_TEXT)
+            .map(
+                dataType -> new TestCaseSupplier(
+                    title + " with " + dataType,
+                    List.of(dataType),
+                    () -> testCase(text.get(), dataType, expectedByteLength)
+                )
+            )
+            .toList();
+    }
+
+    @Override
+    protected Expression build(Source source, List<Expression> args) {
+        assert args.size() == 1;
+        return new ByteLength(source, args.get(0));
+    }
+
+    private static TestCaseSupplier.TestCase testCase(String s, DataType dataType, int expectedByteLength) {
+        var bytesRef = new BytesRef(s);
+        return new TestCaseSupplier.TestCase(
+            List.of(new TestCaseSupplier.TypedData(bytesRef, dataType, "f")),
+            "ByteLengthEvaluator[val=Attribute[channel=0]]",
+            DataType.INTEGER,
+            equalTo(expectedByteLength)
+        );
+    }
+
+    private static final boolean ENTIRELY_NULL_PRESERVES_TYPE = true;
+}

+ 2 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java

@@ -34,6 +34,7 @@ import org.elasticsearch.xpack.esql.expression.function.scalar.date.DateTrunc;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.Abs;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.Pow;
 import org.elasticsearch.xpack.esql.expression.function.scalar.math.Round;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.ByteLength;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.Concat;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.Length;
 import org.elasticsearch.xpack.esql.expression.function.scalar.string.StartsWith;
@@ -115,6 +116,7 @@ public class EvalMapperTests extends ESTestCase {
             new Pow(Source.EMPTY, DOUBLE1, DOUBLE2),
             DOUBLE1,
             literal,
+            new ByteLength(Source.EMPTY, literal),
             new Length(Source.EMPTY, literal),
             new DateFormat(Source.EMPTY, datePattern, DATE, TEST_CONFIG),
             new DateFormat(Source.EMPTY, datePattern, literal, TEST_CONFIG),

+ 5 - 4
x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/60_usage.yml

@@ -30,7 +30,7 @@ setup:
         - method: POST
           path: /_query
           parameters: []
-          capabilities: [ snapshot_test_for_telemetry, fn_bit_length ]
+          capabilities: [ snapshot_test_for_telemetry, fn_byte_length ]
       reason: "Test that should only be executed on snapshot versions"
 
   - do: {xpack.usage: {}}
@@ -91,7 +91,8 @@ setup:
   - match: {esql.functions.cos: $functions_cos}
   - gt: {esql.functions.to_long: $functions_to_long}
   - match: {esql.functions.coalesce: $functions_coalesce}
-  - length: {esql.functions: 119} # check the "sister" test below for a likely update to the same esql.functions length check
+  # Testing for the entire function set isn't feasbile, so we just check that we return the correct count as an approximation.
+  - length: {esql.functions: 120} # check the "sister" test below for a likely update to the same esql.functions length check
 
 ---
 "Basic ESQL usage output (telemetry) non-snapshot version":
@@ -101,7 +102,7 @@ setup:
         - method: POST
           path: /_query
           parameters: []
-          capabilities: [ non_snapshot_test_for_telemetry, fn_bit_length ]
+          capabilities: [ non_snapshot_test_for_telemetry, fn_byte_length ]
       reason: "Test that should only be executed on release versions"
 
   - do: {xpack.usage: {}}
@@ -162,4 +163,4 @@ setup:
   - match: {esql.functions.cos: $functions_cos}
   - gt: {esql.functions.to_long: $functions_to_long}
   - match: {esql.functions.coalesce: $functions_coalesce}
-  - length: {esql.functions: 116} # check the "sister" test above for a likely update to the same esql.functions length check
+  - length: {esql.functions: 117} # check the "sister" test above for a likely update to the same esql.functions length check