diff --git a/docs/changelog/118938.yaml b/docs/changelog/118938.yaml
new file mode 100644
index 0000000000000..395da7912fd4b
--- /dev/null
+++ b/docs/changelog/118938.yaml
@@ -0,0 +1,5 @@
+pr: 118938
+summary: Hash functions
+area: ES|QL
+type: enhancement
+issues: []
diff --git a/docs/reference/esql/functions/description/md5.asciidoc b/docs/reference/esql/functions/description/md5.asciidoc
new file mode 100644
index 0000000000000..2ad847c0ce0e3
--- /dev/null
+++ b/docs/reference/esql/functions/description/md5.asciidoc
@@ -0,0 +1,5 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Description*
+
+Computes the MD5 hash of the input.
diff --git a/docs/reference/esql/functions/description/sha1.asciidoc b/docs/reference/esql/functions/description/sha1.asciidoc
new file mode 100644
index 0000000000000..5bc29f86cc591
--- /dev/null
+++ b/docs/reference/esql/functions/description/sha1.asciidoc
@@ -0,0 +1,5 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Description*
+
+Computes the SHA1 hash of the input.
diff --git a/docs/reference/esql/functions/description/sha256.asciidoc b/docs/reference/esql/functions/description/sha256.asciidoc
new file mode 100644
index 0000000000000..b2a7ef01e1069
--- /dev/null
+++ b/docs/reference/esql/functions/description/sha256.asciidoc
@@ -0,0 +1,5 @@
+// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+*Description*
+
+Computes the SHA256 hash of the input.
diff --git a/docs/reference/esql/functions/examples/hash.asciidoc b/docs/reference/esql/functions/examples/hash.asciidoc
new file mode 100644
index 0000000000000..492e466eb395e
--- /dev/null
+++ b/docs/reference/esql/functions/examples/hash.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}/hash.csv-spec[tag=hash]
+----
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+include::{esql-specs}/hash.csv-spec[tag=hash-result]
+|===
+
diff --git a/docs/reference/esql/functions/examples/md5.asciidoc b/docs/reference/esql/functions/examples/md5.asciidoc
new file mode 100644
index 0000000000000..0b43bc5b791c9
--- /dev/null
+++ b/docs/reference/esql/functions/examples/md5.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}/hash.csv-spec[tag=md5]
+----
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+include::{esql-specs}/hash.csv-spec[tag=md5-result]
+|===
+
diff --git a/docs/reference/esql/functions/examples/sha1.asciidoc b/docs/reference/esql/functions/examples/sha1.asciidoc
new file mode 100644
index 0000000000000..77786431a738a
--- /dev/null
+++ b/docs/reference/esql/functions/examples/sha1.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}/hash.csv-spec[tag=sha1]
+----
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+include::{esql-specs}/hash.csv-spec[tag=sha1-result]
+|===
+
diff --git a/docs/reference/esql/functions/examples/sha256.asciidoc b/docs/reference/esql/functions/examples/sha256.asciidoc
new file mode 100644
index 0000000000000..801c36d8effc8
--- /dev/null
+++ b/docs/reference/esql/functions/examples/sha256.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}/hash.csv-spec[tag=sha256]
+----
+[%header.monospaced.styled,format=dsv,separator=|]
+|===
+include::{esql-specs}/hash.csv-spec[tag=sha256-result]
+|===
+
diff --git a/docs/reference/esql/functions/kibana/definition/hash.json b/docs/reference/esql/functions/kibana/definition/hash.json
index 17a60cf45acfe..dbf4a2542afc5 100644
--- a/docs/reference/esql/functions/kibana/definition/hash.json
+++ b/docs/reference/esql/functions/kibana/definition/hash.json
@@ -77,6 +77,9 @@
"returnType" : "keyword"
}
],
+ "examples" : [
+ "FROM sample_data \n| WHERE message != \"Connection error\"\n| EVAL md5 = hash(\"md5\", message), sha256 = hash(\"sha256\", message) \n| KEEP message, md5, sha256;"
+ ],
"preview" : false,
"snapshot_only" : false
}
diff --git a/docs/reference/esql/functions/kibana/definition/md5.json b/docs/reference/esql/functions/kibana/definition/md5.json
new file mode 100644
index 0000000000000..4d3a88e123ff4
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/definition/md5.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "eval",
+ "name" : "md5",
+ "description" : "Computes the MD5 hash of the input.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "input",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input to hash."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ },
+ {
+ "params" : [
+ {
+ "name" : "input",
+ "type" : "text",
+ "optional" : false,
+ "description" : "Input to hash."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ }
+ ],
+ "examples" : [
+ "FROM sample_data \n| WHERE message != \"Connection error\"\n| EVAL md5 = md5(message)\n| KEEP message, md5;"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/esql/functions/kibana/definition/sha1.json b/docs/reference/esql/functions/kibana/definition/sha1.json
new file mode 100644
index 0000000000000..a6abb31368bb3
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/definition/sha1.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "eval",
+ "name" : "sha1",
+ "description" : "Computes the SHA1 hash of the input.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "input",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input to hash."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ },
+ {
+ "params" : [
+ {
+ "name" : "input",
+ "type" : "text",
+ "optional" : false,
+ "description" : "Input to hash."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ }
+ ],
+ "examples" : [
+ "FROM sample_data \n| WHERE message != \"Connection error\"\n| EVAL sha1 = sha1(message)\n| KEEP message, sha1;"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/esql/functions/kibana/definition/sha256.json b/docs/reference/esql/functions/kibana/definition/sha256.json
new file mode 100644
index 0000000000000..700425d485b61
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/definition/sha256.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "eval",
+ "name" : "sha256",
+ "description" : "Computes the SHA256 hash of the input.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "input",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input to hash."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ },
+ {
+ "params" : [
+ {
+ "name" : "input",
+ "type" : "text",
+ "optional" : false,
+ "description" : "Input to hash."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ }
+ ],
+ "examples" : [
+ "FROM sample_data \n| WHERE message != \"Connection error\"\n| EVAL sha256 = sha256(message)\n| KEEP message, sha256;"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/esql/functions/kibana/docs/hash.md b/docs/reference/esql/functions/kibana/docs/hash.md
index 9826e80ec5bec..4e937778ba67a 100644
--- a/docs/reference/esql/functions/kibana/docs/hash.md
+++ b/docs/reference/esql/functions/kibana/docs/hash.md
@@ -5,3 +5,9 @@ This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../READ
### HASH
Computes the hash of the input using various algorithms such as MD5, SHA, SHA-224, SHA-256, SHA-384, SHA-512.
+```
+FROM sample_data
+| WHERE message != "Connection error"
+| EVAL md5 = hash("md5", message), sha256 = hash("sha256", message)
+| KEEP message, md5, sha256;
+```
diff --git a/docs/reference/esql/functions/kibana/docs/md5.md b/docs/reference/esql/functions/kibana/docs/md5.md
new file mode 100644
index 0000000000000..aacb8a3960165
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/docs/md5.md
@@ -0,0 +1,13 @@
+
+
+### MD5
+Computes the MD5 hash of the input.
+
+```
+FROM sample_data
+| WHERE message != "Connection error"
+| EVAL md5 = md5(message)
+| KEEP message, md5;
+```
diff --git a/docs/reference/esql/functions/kibana/docs/sha1.md b/docs/reference/esql/functions/kibana/docs/sha1.md
new file mode 100644
index 0000000000000..a940aa133f06e
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/docs/sha1.md
@@ -0,0 +1,13 @@
+
+
+### SHA1
+Computes the SHA1 hash of the input.
+
+```
+FROM sample_data
+| WHERE message != "Connection error"
+| EVAL sha1 = sha1(message)
+| KEEP message, sha1;
+```
diff --git a/docs/reference/esql/functions/kibana/docs/sha256.md b/docs/reference/esql/functions/kibana/docs/sha256.md
new file mode 100644
index 0000000000000..fbe576c7c20d6
--- /dev/null
+++ b/docs/reference/esql/functions/kibana/docs/sha256.md
@@ -0,0 +1,13 @@
+
+
+### SHA256
+Computes the SHA256 hash of the input.
+
+```
+FROM sample_data
+| WHERE message != "Connection error"
+| EVAL sha256 = sha256(message)
+| KEEP message, sha256;
+```
diff --git a/docs/reference/esql/functions/layout/hash.asciidoc b/docs/reference/esql/functions/layout/hash.asciidoc
index 27c55ada6319b..daf7fbf1170b2 100644
--- a/docs/reference/esql/functions/layout/hash.asciidoc
+++ b/docs/reference/esql/functions/layout/hash.asciidoc
@@ -12,3 +12,4 @@ image::esql/functions/signature/hash.svg[Embedded,opts=inline]
include::../parameters/hash.asciidoc[]
include::../description/hash.asciidoc[]
include::../types/hash.asciidoc[]
+include::../examples/hash.asciidoc[]
diff --git a/docs/reference/esql/functions/layout/md5.asciidoc b/docs/reference/esql/functions/layout/md5.asciidoc
new file mode 100644
index 0000000000000..82d3031d6bdfd
--- /dev/null
+++ b/docs/reference/esql/functions/layout/md5.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-md5]]
+=== `MD5`
+
+*Syntax*
+
+[.text-center]
+image::esql/functions/signature/md5.svg[Embedded,opts=inline]
+
+include::../parameters/md5.asciidoc[]
+include::../description/md5.asciidoc[]
+include::../types/md5.asciidoc[]
+include::../examples/md5.asciidoc[]
diff --git a/docs/reference/esql/functions/layout/sha1.asciidoc b/docs/reference/esql/functions/layout/sha1.asciidoc
new file mode 100644
index 0000000000000..23e1e0e9ac2ab
--- /dev/null
+++ b/docs/reference/esql/functions/layout/sha1.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-sha1]]
+=== `SHA1`
+
+*Syntax*
+
+[.text-center]
+image::esql/functions/signature/sha1.svg[Embedded,opts=inline]
+
+include::../parameters/sha1.asciidoc[]
+include::../description/sha1.asciidoc[]
+include::../types/sha1.asciidoc[]
+include::../examples/sha1.asciidoc[]
diff --git a/docs/reference/esql/functions/layout/sha256.asciidoc b/docs/reference/esql/functions/layout/sha256.asciidoc
new file mode 100644
index 0000000000000..d36a1345271f5
--- /dev/null
+++ b/docs/reference/esql/functions/layout/sha256.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-sha256]]
+=== `SHA256`
+
+*Syntax*
+
+[.text-center]
+image::esql/functions/signature/sha256.svg[Embedded,opts=inline]
+
+include::../parameters/sha256.asciidoc[]
+include::../description/sha256.asciidoc[]
+include::../types/sha256.asciidoc[]
+include::../examples/sha256.asciidoc[]
diff --git a/docs/reference/esql/functions/parameters/md5.asciidoc b/docs/reference/esql/functions/parameters/md5.asciidoc
new file mode 100644
index 0000000000000..99eba4dc2cb3d
--- /dev/null
+++ b/docs/reference/esql/functions/parameters/md5.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*
+
+`input`::
+Input to hash.
diff --git a/docs/reference/esql/functions/parameters/sha1.asciidoc b/docs/reference/esql/functions/parameters/sha1.asciidoc
new file mode 100644
index 0000000000000..99eba4dc2cb3d
--- /dev/null
+++ b/docs/reference/esql/functions/parameters/sha1.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*
+
+`input`::
+Input to hash.
diff --git a/docs/reference/esql/functions/parameters/sha256.asciidoc b/docs/reference/esql/functions/parameters/sha256.asciidoc
new file mode 100644
index 0000000000000..99eba4dc2cb3d
--- /dev/null
+++ b/docs/reference/esql/functions/parameters/sha256.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*
+
+`input`::
+Input to hash.
diff --git a/docs/reference/esql/functions/signature/md5.svg b/docs/reference/esql/functions/signature/md5.svg
new file mode 100644
index 0000000000000..419af764a212e
--- /dev/null
+++ b/docs/reference/esql/functions/signature/md5.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/esql/functions/signature/sha1.svg b/docs/reference/esql/functions/signature/sha1.svg
new file mode 100644
index 0000000000000..bab03a7eb88c8
--- /dev/null
+++ b/docs/reference/esql/functions/signature/sha1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/esql/functions/signature/sha256.svg b/docs/reference/esql/functions/signature/sha256.svg
new file mode 100644
index 0000000000000..b77126bbefbd8
--- /dev/null
+++ b/docs/reference/esql/functions/signature/sha256.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/esql/functions/string-functions.asciidoc b/docs/reference/esql/functions/string-functions.asciidoc
index da9580a55151a..dd10e4c77581e 100644
--- a/docs/reference/esql/functions/string-functions.asciidoc
+++ b/docs/reference/esql/functions/string-functions.asciidoc
@@ -18,11 +18,14 @@
* <>
* <>
* <>
+* <>
* <>
* <>
* <>
* <>
* <>
+* <>
+* <>
* <>
* <>
* <>
@@ -43,11 +46,14 @@ include::layout/left.asciidoc[]
include::layout/length.asciidoc[]
include::layout/locate.asciidoc[]
include::layout/ltrim.asciidoc[]
+include::layout/md5.asciidoc[]
include::layout/repeat.asciidoc[]
include::layout/replace.asciidoc[]
include::layout/reverse.asciidoc[]
include::layout/right.asciidoc[]
include::layout/rtrim.asciidoc[]
+include::layout/sha1.asciidoc[]
+include::layout/sha256.asciidoc[]
include::layout/space.asciidoc[]
include::layout/split.asciidoc[]
include::layout/starts_with.asciidoc[]
diff --git a/docs/reference/esql/functions/types/md5.asciidoc b/docs/reference/esql/functions/types/md5.asciidoc
new file mode 100644
index 0000000000000..049a553397bbd
--- /dev/null
+++ b/docs/reference/esql/functions/types/md5.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=|]
+|===
+input | result
+keyword | keyword
+text | keyword
+|===
diff --git a/docs/reference/esql/functions/types/sha1.asciidoc b/docs/reference/esql/functions/types/sha1.asciidoc
new file mode 100644
index 0000000000000..049a553397bbd
--- /dev/null
+++ b/docs/reference/esql/functions/types/sha1.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=|]
+|===
+input | result
+keyword | keyword
+text | keyword
+|===
diff --git a/docs/reference/esql/functions/types/sha256.asciidoc b/docs/reference/esql/functions/types/sha256.asciidoc
new file mode 100644
index 0000000000000..049a553397bbd
--- /dev/null
+++ b/docs/reference/esql/functions/types/sha256.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=|]
+|===
+input | result
+keyword | keyword
+text | keyword
+|===
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/hash.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/hash.csv-spec
index fcac1e1859c6d..2614ff09fed06 100644
--- a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/hash.csv-spec
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/hash.csv-spec
@@ -1,18 +1,22 @@
hash
required_capability: hash_function
+// tag::hash[]
FROM sample_data
| WHERE message != "Connection error"
| EVAL md5 = hash("md5", message), sha256 = hash("sha256", message)
| KEEP message, md5, sha256;
+// end::hash[]
ignoreOrder:true
+// tag::hash-result[]
message:keyword | md5:keyword | sha256:keyword
Connected to 10.1.0.1 | abd7d1ce2bb636842a29246b3512dcae | 6d8372129ad78770f7185554dd39864749a62690216460752d6c075fa38ad85c
Connected to 10.1.0.2 | 8f8f1cb60832d153f5b9ec6dc828b93f | b0db24720f15857091b3c99f4c4833586d0ea3229911b8777efb8d917cf27e9a
Connected to 10.1.0.3 | 912b6dc13503165a15de43304bb77c78 | 75b0480188db8acc4d5cc666a51227eb2bc5b989cd8ca912609f33e0846eff57
Disconnected | ef70e46fd3bbc21e3e1f0b6815e750c0 | 04dfac3671b494ad53fcd152f7a14511bfb35747278aad8ce254a0d6e4ba4718
;
+// end::hash-result[]
hashOfConvertedType
@@ -94,12 +98,75 @@ input:integer | md5:keyword | sha256:keyword
hashWithStats
required_capability: hash_function
+required_capability: hash_function_aliases_v1
FROM sample_data
| EVAL md5="md5"
-| STATS count = count(*) by hash(md5, message)
+| STATS count = count(*) by hash(md5, message), md5(message), sha1(message), sha256(message)
| WHERE count > 1;
-count:long | hash(md5, message):keyword
-3 | 2e92ae79ff32b37fee4368a594792183
+count:long | hash(md5, message):keyword | md5(message):keyword | sha1(message):keyword | sha256(message):keyword
+3 | 2e92ae79ff32b37fee4368a594792183 | 2e92ae79ff32b37fee4368a594792183 | 1dbb3521876a899f82d6b0ff10eb32a01e03aba8 | 8d137af9c64fba09bbb003aba93f0b029898fe19e7927cd696f4c3e2b69f538d
;
+
+
+md5Hash
+required_capability: hash_function_aliases_v1
+
+// tag::md5[]
+FROM sample_data
+| WHERE message != "Connection error"
+| EVAL md5 = md5(message)
+| KEEP message, md5;
+// end::md5[]
+ignoreOrder:true
+
+// tag::md5-result[]
+message:keyword | md5:keyword
+Connected to 10.1.0.1 | abd7d1ce2bb636842a29246b3512dcae
+Connected to 10.1.0.2 | 8f8f1cb60832d153f5b9ec6dc828b93f
+Connected to 10.1.0.3 | 912b6dc13503165a15de43304bb77c78
+Disconnected | ef70e46fd3bbc21e3e1f0b6815e750c0
+;
+// end::md5-result[]
+
+
+sha1Hash
+required_capability: hash_function_aliases_v1
+
+// tag::sha1[]
+FROM sample_data
+| WHERE message != "Connection error"
+| EVAL sha1 = sha1(message)
+| KEEP message, sha1;
+// end::sha1[]
+ignoreOrder:true
+
+// tag::sha1-result[]
+message:keyword | sha1:keyword
+Connected to 10.1.0.1 | 42b85531a79088036a17759db7d2de292b92f57f
+Connected to 10.1.0.2 | d30db445da2e9237c9718d0c7e4fb7cbbe9c2cb4
+Connected to 10.1.0.3 | 2733848d943809f0b10cad3e980763e88afb9853
+Disconnected | 771e05f27b99fd59f638f41a7a4e977b1d4691fe
+;
+// end::sha1-result[]
+
+sha256Hash
+required_capability: hash_function_aliases_v1
+
+// tag::sha256[]
+FROM sample_data
+| WHERE message != "Connection error"
+| EVAL sha256 = sha256(message)
+| KEEP message, sha256;
+// end::sha256[]
+ignoreOrder:true
+
+// tag::sha256-result[]
+message:keyword | sha256:keyword
+Connected to 10.1.0.1 | 6d8372129ad78770f7185554dd39864749a62690216460752d6c075fa38ad85c
+Connected to 10.1.0.2 | b0db24720f15857091b3c99f4c4833586d0ea3229911b8777efb8d917cf27e9a
+Connected to 10.1.0.3 | 75b0480188db8acc4d5cc666a51227eb2bc5b989cd8ca912609f33e0846eff57
+Disconnected | 04dfac3671b494ad53fcd152f7a14511bfb35747278aad8ce254a0d6e4ba4718
+;
+// end::sha256-result[]
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
index d423453ac5ce3..ef82ca47804bc 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java
@@ -451,6 +451,10 @@ public enum Cap {
* Hash function
*/
HASH_FUNCTION,
+ /**
+ * Hash function aliases such as MD5
+ */
+ HASH_FUNCTION_ALIASES_V1,
/**
* Don't optimize CASE IS NOT NULL function by not requiring the fields to be not null as well.
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java
index 908c9c5f197a8..d310973d6dbe7 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java
@@ -134,11 +134,14 @@
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Left;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Length;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Locate;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.Md5;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.RTrim;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Repeat;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Replace;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Reverse;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Right;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.Sha1;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.Sha256;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Space;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Split;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.StartsWith;
@@ -333,11 +336,14 @@ private static FunctionDefinition[][] functions() {
def(Left.class, Left::new, "left"),
def(Length.class, Length::new, "length"),
def(Locate.class, Locate::new, "locate"),
+ def(Md5.class, Md5::new, "md5"),
def(RTrim.class, RTrim::new, "rtrim"),
def(Repeat.class, Repeat::new, "repeat"),
def(Replace.class, Replace::new, "replace"),
def(Reverse.class, Reverse::new, "reverse"),
def(Right.class, Right::new, "right"),
+ def(Sha1.class, Sha1::new, "sha1"),
+ def(Sha256.class, Sha256::new, "sha256"),
def(Space.class, Space::new, "space"),
def(StartsWith.class, StartsWith::new, "starts_with"),
def(Substring.class, Substring::new, "substring"),
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java
index 3cf0eef9074ad..c4b9f6885e617 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java
@@ -37,10 +37,13 @@
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Hash;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Left;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Locate;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.Md5;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Repeat;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Replace;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Reverse;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Right;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.Sha1;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.Sha256;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Split;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.StartsWith;
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Substring;
@@ -79,6 +82,7 @@ public static List getNamedWriteables() {
entries.add(Left.ENTRY);
entries.add(Locate.ENTRY);
entries.add(Log.ENTRY);
+ entries.add(Md5.ENTRY);
entries.add(Now.ENTRY);
entries.add(Or.ENTRY);
entries.add(Pi.ENTRY);
@@ -88,6 +92,8 @@ public static List getNamedWriteables() {
entries.add(Replace.ENTRY);
entries.add(Reverse.ENTRY);
entries.add(Round.ENTRY);
+ entries.add(Sha1.ENTRY);
+ entries.add(Sha256.ENTRY);
entries.add(Split.ENTRY);
entries.add(Substring.ENTRY);
entries.add(StartsWith.ENTRY);
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/AbstractHashFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/AbstractHashFunction.java
new file mode 100644
index 0000000000000..a39f0a4f32db2
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/AbstractHashFunction.java
@@ -0,0 +1,70 @@
+/*
+ * 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.common.io.stream.StreamInput;
+import org.elasticsearch.compute.operator.BreakingBytesRefBuilder;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+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.scalar.UnaryScalarFunction;
+import org.elasticsearch.xpack.esql.expression.function.scalar.string.Hash.HashFunction;
+
+import java.io.IOException;
+import java.util.function.Function;
+
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT;
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString;
+
+public abstract class AbstractHashFunction extends UnaryScalarFunction {
+
+ protected AbstractHashFunction(Source source, Expression field) {
+ super(source, field);
+ }
+
+ protected AbstractHashFunction(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ protected abstract HashFunction getHashFunction();
+
+ @Override
+ public DataType dataType() {
+ return DataType.KEYWORD;
+ }
+
+ @Override
+ protected TypeResolution resolveType() {
+ if (childrenResolved() == false) {
+ return new TypeResolution("Unresolved children");
+ }
+ return isString(field, sourceText(), DEFAULT);
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
+ return new HashConstantEvaluator.Factory(
+ source(),
+ context -> new BreakingBytesRefBuilder(context.breaker(), "hash"),
+ new Function<>() {
+ @Override
+ public HashFunction apply(DriverContext context) {
+ return getHashFunction().copy();
+ }
+
+ @Override
+ public String toString() {
+ return getHashFunction().toString();
+ }
+ },
+ toEvaluator.apply(field)
+ );
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Hash.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Hash.java
index 99c5908699ec2..52d33c0fc9d3d 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Hash.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Hash.java
@@ -21,6 +21,7 @@
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.EsqlScalarFunction;
@@ -46,7 +47,8 @@ public class Hash extends EsqlScalarFunction {
@FunctionInfo(
returnType = "keyword",
- description = "Computes the hash of the input using various algorithms such as MD5, SHA, SHA-224, SHA-256, SHA-384, SHA-512."
+ description = "Computes the hash of the input using various algorithms such as MD5, SHA, SHA-224, SHA-256, SHA-384, SHA-512.",
+ examples = { @Example(file = "hash", tag = "hash") }
)
public Hash(
Source source,
@@ -186,10 +188,18 @@ protected NodeInfo extends Expression> info() {
public record HashFunction(String algorithm, MessageDigest digest) {
+ public static HashFunction create(String algorithm) {
+ try {
+ return new HashFunction(algorithm, MessageDigest.getInstance(algorithm));
+ } catch (NoSuchAlgorithmException e) {
+ assert false : "Expected to create a valid hashing algorithm";
+ throw new IllegalStateException(e);
+ }
+ }
+
public static HashFunction create(BytesRef literal) throws NoSuchAlgorithmException {
var algorithm = literal.utf8ToString();
- var digest = MessageDigest.getInstance(algorithm);
- return new HashFunction(algorithm, digest);
+ return new HashFunction(algorithm, MessageDigest.getInstance(algorithm));
}
public HashFunction copy() {
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5.java
new file mode 100644
index 0000000000000..b42ec1036cb5b
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5.java
@@ -0,0 +1,61 @@
+/*
+ * 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.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+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.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.string.Hash.HashFunction;
+
+import java.io.IOException;
+import java.util.List;
+
+public class Md5 extends AbstractHashFunction {
+
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "MD5", Md5::new);
+
+ private static final HashFunction MD5 = HashFunction.create("MD5");
+
+ @FunctionInfo(
+ returnType = "keyword",
+ description = "Computes the MD5 hash of the input.",
+ examples = { @Example(file = "hash", tag = "md5") }
+ )
+ public Md5(Source source, @Param(name = "input", type = { "keyword", "text" }, description = "Input to hash.") Expression input) {
+ super(source, input);
+ }
+
+ private Md5(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ protected HashFunction getHashFunction() {
+ return MD5;
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new Md5(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, Md5::new, field);
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Sha1.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Sha1.java
new file mode 100644
index 0000000000000..ba1f62562c03a
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Sha1.java
@@ -0,0 +1,60 @@
+/*
+ * 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.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+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.expression.function.Example;
+import org.elasticsearch.xpack.esql.expression.function.FunctionInfo;
+import org.elasticsearch.xpack.esql.expression.function.Param;
+
+import java.io.IOException;
+import java.util.List;
+
+public class Sha1 extends AbstractHashFunction {
+
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "SHA1", Sha1::new);
+
+ private static final Hash.HashFunction SHA1 = Hash.HashFunction.create("SHA1");
+
+ @FunctionInfo(
+ returnType = "keyword",
+ description = "Computes the SHA1 hash of the input.",
+ examples = { @Example(file = "hash", tag = "sha1") }
+ )
+ public Sha1(Source source, @Param(name = "input", type = { "keyword", "text" }, description = "Input to hash.") Expression input) {
+ super(source, input);
+ }
+
+ private Sha1(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ protected Hash.HashFunction getHashFunction() {
+ return SHA1;
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new Sha1(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, Sha1::new, field);
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Sha256.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Sha256.java
new file mode 100644
index 0000000000000..b16767a3f7948
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Sha256.java
@@ -0,0 +1,60 @@
+/*
+ * 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.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+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.expression.function.Example;
+import org.elasticsearch.xpack.esql.expression.function.FunctionInfo;
+import org.elasticsearch.xpack.esql.expression.function.Param;
+
+import java.io.IOException;
+import java.util.List;
+
+public class Sha256 extends AbstractHashFunction {
+
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "SHA256", Sha256::new);
+
+ private static final Hash.HashFunction SHA256 = Hash.HashFunction.create("SHA256");
+
+ @FunctionInfo(
+ returnType = "keyword",
+ description = "Computes the SHA256 hash of the input.",
+ examples = { @Example(file = "hash", tag = "sha256") }
+ )
+ public Sha256(Source source, @Param(name = "input", type = { "keyword", "text" }, description = "Input to hash.") Expression input) {
+ super(source, input);
+ }
+
+ private Sha256(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ protected Hash.HashFunction getHashFunction() {
+ return SHA256;
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new Sha256(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, Sha256::new, field);
+ }
+}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/HashTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/HashTests.java
index c25270474959b..fe62b6d7b4f1b 100644
--- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/HashTests.java
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/HashTests.java
@@ -87,12 +87,22 @@ private static TestCaseSupplier createTestCase(String algorithm, boolean forceLi
});
}
+ static void addHashFunctionTestCases(List cases, String algorithm) {
+ TestCaseSupplier.forUnaryStrings(
+ cases,
+ "HashConstantEvaluator[algorithm=" + algorithm + ", input=Attribute[channel=0]]",
+ DataType.KEYWORD,
+ input -> new BytesRef(HashTests.hash(algorithm, BytesRefs.toString(input))),
+ List.of()
+ );
+ }
+
private static TestCaseSupplier.TypedData createTypedData(String value, boolean forceLiteral, DataType type, String name) {
var data = new TestCaseSupplier.TypedData(new BytesRef(value), type, name);
return forceLiteral ? data.forceLiteral() : data;
}
- private static String hash(String algorithm, String input) {
+ static String hash(String algorithm, String input) {
try {
return HexFormat.of().formatHex(MessageDigest.getInstance(algorithm).digest(input.getBytes(StandardCharsets.UTF_8)));
} catch (NoSuchAlgorithmException e) {
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5ErrorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5ErrorTests.java
new file mode 100644
index 0000000000000..701cef748c932
--- /dev/null
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5ErrorTests.java
@@ -0,0 +1,38 @@
+/*
+ * 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.core.type.DataType;
+import org.elasticsearch.xpack.esql.expression.function.ErrorsForCasesWithoutExamplesTestCase;
+import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
+import org.hamcrest.Matcher;
+
+import java.util.List;
+import java.util.Set;
+
+import static org.hamcrest.Matchers.equalTo;
+
+public class Md5ErrorTests extends ErrorsForCasesWithoutExamplesTestCase {
+
+ @Override
+ protected List cases() {
+ return paramsToSuppliers(Md5Tests.parameters());
+ }
+
+ @Override
+ protected Expression build(Source source, List args) {
+ return new Md5(source, args.get(0));
+ }
+
+ @Override
+ protected Matcher expectedTypeErrorMatcher(List> validPerPosition, List signature) {
+ return equalTo(typeErrorMessage(false, validPerPosition, signature, (v, p) -> "string"));
+ }
+}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5SerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5SerializationTests.java
new file mode 100644
index 0000000000000..666e5a3ea5d58
--- /dev/null
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5SerializationTests.java
@@ -0,0 +1,25 @@
+/*
+ * 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.expression.AbstractExpressionSerializationTests;
+
+import java.io.IOException;
+
+public class Md5SerializationTests extends AbstractExpressionSerializationTests {
+
+ @Override
+ protected Md5 createTestInstance() {
+ return new Md5(randomSource(), randomChild());
+ }
+
+ @Override
+ protected Md5 mutateInstance(Md5 instance) throws IOException {
+ return new Md5(instance.source(), mutateExpression(instance.field()));
+ }
+}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5Tests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5Tests.java
new file mode 100644
index 0000000000000..3c0a2067a81b0
--- /dev/null
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5Tests.java
@@ -0,0 +1,39 @@
+/*
+ * 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.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+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;
+
+public class Md5Tests extends AbstractScalarFunctionTestCase {
+
+ public Md5Tests(@Name("TestCase") Supplier testCaseSupplier) {
+ this.testCase = testCaseSupplier.get();
+ }
+
+ @ParametersFactory
+ public static Iterable