diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/description/clamp.md
new file mode 100644
index 0000000000000..5d7d56931af88
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/clamp.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Clamps the values of all samples to have a lower limit of min and an upper limit of max.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/description/clamp_max.md
new file mode 100644
index 0000000000000..d2cc12be612cf
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/clamp_max.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Returns clamps the values of all input samples clamped to have an upper limit of max.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/description/clamp_min.md
new file mode 100644
index 0000000000000..5fa0df5c1ce4f
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/clamp_min.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Returns clamps the values of all input samples clamped to have a lower limit of min.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp.md
new file mode 100644
index 0000000000000..754111da85fc3
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp.md
@@ -0,0 +1,20 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+TS k8s
+| STATS bytes_in = sum(network.bytes_in),
+ // our network cards are rate-limited to 200bps so we clamp to that (just an example)
+ clamped_network_bytes_in = sum(clamp(network.bytes_in, 0, 200))
+ BY time_bucket = bucket(@timestamp,1minute)
+```
+
+| bytes_in:long | clamped_network_bytes_in:long | time_bucket:datetime |
+| --- | --- | --- |
+| 1849 | 600 | 2024-05-10T00:00:00.000Z |
+| 2334 | 800 | 2024-05-10T00:01:00.000Z |
+| 3487 | 1178 | 2024-05-10T00:02:00.000Z |
+| 2941 | 861 | 2024-05-10T00:03:00.000Z |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md
new file mode 100644
index 0000000000000..d4d25c611aa85
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md
@@ -0,0 +1,19 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+TS k8s
+| EVAL full_clamped_cost = clamp(network.cost, 1, 20)
+| KEEP full_clamped_cost, @timestamp
+```
+
+| full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_bucket:datetime |
+| --- | --- | --- | --- |
+| 18.0 | 9.0 | 94.875 | 2024-05-10T00:09:00.000Z |
+| 15.25 | 8.0 | 84.125 | 2024-05-10T00:08:00.000Z |
+| 15.0 | 8.0 | 83.5 | 2024-05-10T00:15:00.000Z |
+| 13.75 | 7.0 | 71.625 | 2024-05-10T00:22:00.000Z |
+| 13.125 | 7.5 | 90.5 | 2024-05-10T00:18:00.000Z |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md
new file mode 100644
index 0000000000000..2d4edc73a8e96
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md
@@ -0,0 +1,17 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+FROM k8s
+| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute)
+```
+
+| full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_bucket:datetime |
+| --- | --- | --- | --- |
+| 39.0 | 20.0 | 206.25 | 2024-05-10T00:09:00.000Z |
+| 29.125 | 15.5 | 173.0 | 2024-05-10T00:18:00.000Z |
+| 28.0 | 14.125 | 155.625 | 2024-05-10T00:17:00.000Z |
+| 23.25 | 12.0 | 124.875 | 2024-05-10T00:08:00.000Z |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp.md
new file mode 100644
index 0000000000000..1e99c49038c2e
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+## `CLAMP` [esql-clamp]
+
+**Syntax**
+
+:::{image} ../../../images/functions/clamp.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/clamp.md
+:::
+
+:::{include} ../description/clamp.md
+:::
+
+:::{include} ../types/clamp.md
+:::
+
+:::{include} ../examples/clamp.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_max.md
new file mode 100644
index 0000000000000..05c6daafb4f83
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_max.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+## `CLAMP_MAX` [esql-clamp_max]
+
+**Syntax**
+
+:::{image} ../../../images/functions/clamp_max.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/clamp_max.md
+:::
+
+:::{include} ../description/clamp_max.md
+:::
+
+:::{include} ../types/clamp_max.md
+:::
+
+:::{include} ../examples/clamp_max.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_min.md
new file mode 100644
index 0000000000000..d592eab4faf8c
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_min.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+## `CLAMP_MIN` [esql-clamp_min]
+
+**Syntax**
+
+:::{image} ../../../images/functions/clamp_min.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/clamp_min.md
+:::
+
+:::{include} ../description/clamp_min.md
+:::
+
+:::{include} ../types/clamp_min.md
+:::
+
+:::{include} ../examples/clamp_min.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp.md
new file mode 100644
index 0000000000000..68bfe9eeba491
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp.md
@@ -0,0 +1,13 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`field`
+: Numeric expression. If `null`, the function returns `null`.
+
+`min`
+: The min value to clamp data into.
+
+`max`
+: The max value to clamp data into.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_max.md
new file mode 100644
index 0000000000000..9137ac5aee8db
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_max.md
@@ -0,0 +1,10 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`field`
+: field to clamp.
+
+`max`
+: The max value to clamp data into.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_min.md
new file mode 100644
index 0000000000000..b878673b0a99a
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_min.md
@@ -0,0 +1,10 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`field`
+: field to clamp.
+
+`min`
+: The min value to clamp data into.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/types/clamp.md
new file mode 100644
index 0000000000000..5b76a0978788b
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/clamp.md
@@ -0,0 +1,16 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| field | min | max | result |
+| --- | --- | --- | --- |
+| boolean | boolean | boolean | boolean |
+| date | date | date | date |
+| double | double | double | double |
+| integer | integer | integer | integer |
+| ip | ip | ip | ip |
+| keyword | keyword | keyword | keyword |
+| long | long | long | long |
+| unsigned_long | unsigned_long | unsigned_long | unsigned_long |
+| version | version | version | version |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/types/clamp_max.md
new file mode 100644
index 0000000000000..3a3af0e670f7b
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/clamp_max.md
@@ -0,0 +1,16 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| field | max | result |
+| --- | --- | --- |
+| boolean | boolean | boolean |
+| date | date | date |
+| double | double | double |
+| integer | integer | integer |
+| ip | ip | ip |
+| keyword | keyword | keyword |
+| long | long | long |
+| unsigned_long | unsigned_long | unsigned_long |
+| version | version | version |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/types/clamp_min.md
new file mode 100644
index 0000000000000..0100d810fea52
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/clamp_min.md
@@ -0,0 +1,16 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| field | min | result |
+| --- | --- | --- |
+| boolean | boolean | boolean |
+| date | date | date |
+| double | double | double |
+| integer | integer | integer |
+| ip | ip | ip |
+| keyword | keyword | keyword |
+| long | long | long |
+| unsigned_long | unsigned_long | unsigned_long |
+| version | version | version |
+
diff --git a/docs/reference/query-languages/esql/images/functions/clamp.svg b/docs/reference/query-languages/esql/images/functions/clamp.svg
new file mode 100644
index 0000000000000..36cb2c79c0f63
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/clamp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/clamp_max.svg b/docs/reference/query-languages/esql/images/functions/clamp_max.svg
new file mode 100644
index 0000000000000..77db4f490a5ec
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/clamp_max.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/clamp_min.svg b/docs/reference/query-languages/esql/images/functions/clamp_min.svg
new file mode 100644
index 0000000000000..982dc156cc458
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/clamp_min.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/clamp.json b/docs/reference/query-languages/esql/kibana/definition/functions/clamp.json
new file mode 100644
index 0000000000000..8650cb54878fe
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/clamp.json
@@ -0,0 +1,229 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "clamp",
+ "description" : "Clamps the values of all samples to have a lower limit of min and an upper limit of max.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "boolean",
+ "optional" : false,
+ "description" : "Numeric expression. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "min",
+ "type" : "boolean",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ },
+ {
+ "name" : "max",
+ "type" : "boolean",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "date",
+ "optional" : false,
+ "description" : "Numeric expression. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "min",
+ "type" : "date",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ },
+ {
+ "name" : "max",
+ "type" : "date",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "date"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "double",
+ "optional" : false,
+ "description" : "Numeric expression. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "min",
+ "type" : "double",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ },
+ {
+ "name" : "max",
+ "type" : "double",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "double"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "Numeric expression. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "min",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ },
+ {
+ "name" : "max",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "integer"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "ip",
+ "optional" : false,
+ "description" : "Numeric expression. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "min",
+ "type" : "ip",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ },
+ {
+ "name" : "max",
+ "type" : "ip",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "ip"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Numeric expression. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "min",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ },
+ {
+ "name" : "max",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Numeric expression. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "min",
+ "type" : "long",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ },
+ {
+ "name" : "max",
+ "type" : "long",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "unsigned_long",
+ "optional" : false,
+ "description" : "Numeric expression. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "min",
+ "type" : "unsigned_long",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ },
+ {
+ "name" : "max",
+ "type" : "unsigned_long",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "unsigned_long"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "version",
+ "optional" : false,
+ "description" : "Numeric expression. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "min",
+ "type" : "version",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ },
+ {
+ "name" : "max",
+ "type" : "version",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "version"
+ }
+ ],
+ "examples" : [
+ "TS k8s\n| STATS bytes_in = sum(network.bytes_in),\n // our network cards are rate-limited to 200bps so we clamp to that (just an example)\n clamped_network_bytes_in = sum(clamp(network.bytes_in, 0, 200))\n BY time_bucket = bucket(@timestamp,1minute)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json
new file mode 100644
index 0000000000000..594cf22e0c931
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json
@@ -0,0 +1,175 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "clamp_max",
+ "description" : "Returns clamps the values of all input samples clamped to have an upper limit of max.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "boolean",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "max",
+ "type" : "boolean",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "date",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "max",
+ "type" : "date",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "date"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "double",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "max",
+ "type" : "double",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "double"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "max",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "integer"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "ip",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "max",
+ "type" : "ip",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "ip"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "max",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "long",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "max",
+ "type" : "long",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "unsigned_long",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "max",
+ "type" : "unsigned_long",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "unsigned_long"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "version",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "max",
+ "type" : "version",
+ "optional" : false,
+ "description" : "The max value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "version"
+ }
+ ],
+ "examples" : [
+ "TS k8s\n| EVAL full_clamped_cost = clamp(network.cost, 1, 20)\n| KEEP full_clamped_cost, @timestamp"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json
new file mode 100644
index 0000000000000..5572b05f3a9bb
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json
@@ -0,0 +1,175 @@
+{
+ "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "clamp_min",
+ "description" : "Returns clamps the values of all input samples clamped to have a lower limit of min.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "boolean",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "min",
+ "type" : "boolean",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "boolean"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "date",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "min",
+ "type" : "date",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "date"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "double",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "min",
+ "type" : "double",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "double"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "min",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "integer"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "ip",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "min",
+ "type" : "ip",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "ip"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "min",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "long",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "min",
+ "type" : "long",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "unsigned_long",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "min",
+ "type" : "unsigned_long",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "unsigned_long"
+ },
+ {
+ "params" : [
+ {
+ "name" : "field",
+ "type" : "version",
+ "optional" : false,
+ "description" : "field to clamp."
+ },
+ {
+ "name" : "min",
+ "type" : "version",
+ "optional" : false,
+ "description" : "The min value to clamp data into."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "version"
+ }
+ ],
+ "examples" : [
+ "FROM k8s\n| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/clamp.md b/docs/reference/query-languages/esql/kibana/docs/functions/clamp.md
new file mode 100644
index 0000000000000..b4e3300f8e3b9
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/clamp.md
@@ -0,0 +1,12 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+### CLAMP
+Clamps the values of all samples to have a lower limit of min and an upper limit of max.
+
+```esql
+TS k8s
+| STATS bytes_in = sum(network.bytes_in),
+ // our network cards are rate-limited to 200bps so we clamp to that (just an example)
+ clamped_network_bytes_in = sum(clamp(network.bytes_in, 0, 200))
+ BY time_bucket = bucket(@timestamp,1minute)
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md
new file mode 100644
index 0000000000000..793a219d5a682
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md
@@ -0,0 +1,10 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+### CLAMP MAX
+Returns clamps the values of all input samples clamped to have an upper limit of max.
+
+```esql
+TS k8s
+| EVAL full_clamped_cost = clamp(network.cost, 1, 20)
+| KEEP full_clamped_cost, @timestamp
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md
new file mode 100644
index 0000000000000..c267ec1aac292
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.
+
+### CLAMP MIN
+Returns clamps the values of all input samples clamped to have a lower limit of min.
+
+```esql
+FROM k8s
+| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute)
+```
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/k8s-timeseries-clamp.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/k8s-timeseries-clamp.csv-spec
new file mode 100644
index 0000000000000..ccfdffff2745d
--- /dev/null
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/k8s-timeseries-clamp.csv-spec
@@ -0,0 +1,100 @@
+clamp_of_double_no_grouping
+required_capability: ts_command_v0
+required_capability: clamp_functions
+// tag::clamp-min[]
+FROM k8s
+| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute)
+// end::clamp-min[]
+| SORT full_clamped_cost DESC, time_bucket DESC | LIMIT 10;
+
+// tag::clamp-min-result[]
+full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_bucket:datetime
+39.0 | 20.0 | 206.25 | 2024-05-10T00:09:00.000Z
+29.125 | 15.5 | 173.0 | 2024-05-10T00:18:00.000Z
+28.0 | 14.125 | 155.625 | 2024-05-10T00:17:00.000Z
+23.25 | 12.0 | 124.875 | 2024-05-10T00:08:00.000Z
+// end::clamp-min-result[]
+20.625 | 11.0 | 113.5 | 2024-05-10T00:15:00.000Z
+19.0 | 9.625 | 105.375 | 2024-05-10T00:06:00.000Z
+17.75 | 9.0 | 91.625 | 2024-05-10T00:22:00.000Z
+17.625 | 9.0 | 91.25 | 2024-05-10T00:13:00.000Z
+16.375 | 8.625 | 100.0 | 2024-05-10T00:20:00.000Z
+16.0 | 8.125 | 91.25 | 2024-05-10T00:14:00.000Z
+
+;
+
+clamp_of_double_no_grouping
+required_capability: ts_command_v0
+required_capability: clamp_functions
+// tag::clamp-max[]
+TS k8s
+| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute)
+// end::clamp-max[]
+| SORT full_clamped_cost DESC, time_bucket DESC | LIMIT 10;
+
+// tag::clamp-max-result[]
+full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_bucket:datetime
+18.0 | 9.0 | 94.875 | 2024-05-10T00:09:00.000Z
+15.25 | 8.0 | 84.125 | 2024-05-10T00:08:00.000Z
+15.0 | 8.0 | 83.5 | 2024-05-10T00:15:00.000Z
+13.75 | 7.0 | 71.625 | 2024-05-10T00:22:00.000Z
+13.125 | 7.5 | 90.5 | 2024-05-10T00:18:00.000Z
+// end::clamp-max-result[]
+13.0 | 6.5 | 75.625 | 2024-05-10T00:17:00.000Z
+12.0 | 5.875 | 70.0 | 2024-05-10T00:20:00.000Z
+12.0 | 6.125 | 70.0 | 2024-05-10T00:14:00.000Z
+12.0 | 6.0 | 61.25 | 2024-05-10T00:13:00.000Z
+12.0 | 6.0 | 60.0 | 2024-05-10T00:02:00.000Z
+
+;
+
+clamp_of_long
+required_capability: ts_command_v0
+required_capability: clamp_functions
+// tag::clamp[]
+TS k8s
+| STATS bytes_in = sum(network.bytes_in),
+ // our network cards are rate-limited to 200bps so we clamp to that (just an example)
+ clamped_network_bytes_in = sum(clamp(network.bytes_in, 0, 200))
+ BY time_bucket = bucket(@timestamp,1minute)
+// end::clamp[]
+| SORT time_bucket, bytes_in DESC | LIMIT 10;
+
+// tag::clamp-result[]
+bytes_in:long | clamped_network_bytes_in:long | time_bucket:datetime
+1849 | 600 | 2024-05-10T00:00:00.000Z
+2334 | 800 | 2024-05-10T00:01:00.000Z
+3487 | 1178 | 2024-05-10T00:02:00.000Z
+2941 | 861 | 2024-05-10T00:03:00.000Z
+// end::clamp-result[]
+1557 | 403 | 2024-05-10T00:04:00.000Z
+1144 | 434 | 2024-05-10T00:05:00.000Z
+3594 | 1200 | 2024-05-10T00:06:00.000Z
+1353 | 401 | 2024-05-10T00:07:00.000Z
+3188 | 1295 | 2024-05-10T00:08:00.000Z
+3850 | 1468 | 2024-05-10T00:09:00.000Z
+
+;
+
+clamp_without_stats_command
+required_capability: clamp_functions
+// tag::clamp[]
+TS k8s
+| EVAL full_clamped_cost = clamp(network.cost, 1, 20)
+| KEEP full_clamped_cost, @timestamp
+// end::clamp-max[]
+| SORT full_clamped_cost DESC, @timestamp | LIMIT 10;
+
+// tag::clamp-max-result[]
+full_clamped_cost:double | @timestamp:datetime
+12.5 | 2024-05-10T00:09:52.000Z
+12.375 | 2024-05-10T00:01:29.000Z
+12.375 | 2024-05-10T00:06:42.000Z
+12.375 | 2024-05-10T00:17:12.000Z
+12.25 | 2024-05-10T00:19:48.000Z
+12.125 | 2024-05-10T00:00:57.000Z
+12.125 | 2024-05-10T00:03:26.000Z
+12.125 | 2024-05-10T00:07:19.000Z
+12.125 | 2024-05-10T00:17:39.000Z
+12.0 | 2024-05-10T00:08:08.000Z
+;
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBooleanEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBooleanEvaluator.java
new file mode 100644
index 0000000000000..8a828c781e6db
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBooleanEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BooleanBlock;
+import org.elasticsearch.compute.data.BooleanVector;
+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 ClampMax}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMaxBooleanEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxBooleanEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator max;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMaxBooleanEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator max, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BooleanBlock fieldBlock = (BooleanBlock) field.eval(page)) {
+ try (BooleanBlock maxBlock = (BooleanBlock) max.eval(page)) {
+ BooleanVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ BooleanVector maxVector = maxBlock.asVector();
+ if (maxVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, maxVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += max.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public BooleanBlock eval(int positionCount, BooleanBlock fieldBlock, BooleanBlock maxBlock) {
+ try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (maxBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ boolean field = fieldBlock.getBoolean(fieldBlock.getFirstValueIndex(p));
+ boolean max = maxBlock.getBoolean(maxBlock.getFirstValueIndex(p));
+ result.appendBoolean(ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ public BooleanVector eval(int positionCount, BooleanVector fieldVector, BooleanVector maxVector) {
+ try(BooleanVector.FixedBuilder result = driverContext.blockFactory().newBooleanVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean field = fieldVector.getBoolean(p);
+ boolean max = maxVector.getBoolean(p);
+ result.appendBoolean(p, ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxBooleanEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, max);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory max;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory max) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ }
+
+ @Override
+ public ClampMaxBooleanEvaluator get(DriverContext context) {
+ return new ClampMaxBooleanEvaluator(source, field.get(context), max.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxBooleanEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBytesRefEvaluator.java
new file mode 100644
index 0000000000000..38d47429edcc5
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBytesRefEvaluator.java
@@ -0,0 +1,167 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+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 ClampMax}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMaxBytesRefEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxBytesRefEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator max;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMaxBytesRefEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator max, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock fieldBlock = (BytesRefBlock) field.eval(page)) {
+ try (BytesRefBlock maxBlock = (BytesRefBlock) max.eval(page)) {
+ BytesRefVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ BytesRefVector maxVector = maxBlock.asVector();
+ if (maxVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, maxVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += max.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public BytesRefBlock eval(int positionCount, BytesRefBlock fieldBlock, BytesRefBlock maxBlock) {
+ try(BytesRefBlock.Builder result = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ BytesRef fieldScratch = new BytesRef();
+ BytesRef maxScratch = new BytesRef();
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (maxBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ BytesRef field = fieldBlock.getBytesRef(fieldBlock.getFirstValueIndex(p), fieldScratch);
+ BytesRef max = maxBlock.getBytesRef(maxBlock.getFirstValueIndex(p), maxScratch);
+ result.appendBytesRef(ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ public BytesRefVector eval(int positionCount, BytesRefVector fieldVector,
+ BytesRefVector maxVector) {
+ try(BytesRefVector.Builder result = driverContext.blockFactory().newBytesRefVectorBuilder(positionCount)) {
+ BytesRef fieldScratch = new BytesRef();
+ BytesRef maxScratch = new BytesRef();
+ position: for (int p = 0; p < positionCount; p++) {
+ BytesRef field = fieldVector.getBytesRef(p, fieldScratch);
+ BytesRef max = maxVector.getBytesRef(p, maxScratch);
+ result.appendBytesRef(ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxBytesRefEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, max);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory max;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory max) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ }
+
+ @Override
+ public ClampMaxBytesRefEvaluator get(DriverContext context) {
+ return new ClampMaxBytesRefEvaluator(source, field.get(context), max.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxBytesRefEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxDoubleEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxDoubleEvaluator.java
new file mode 100644
index 0000000000000..f931e15956179
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxDoubleEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.DoubleBlock;
+import org.elasticsearch.compute.data.DoubleVector;
+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 ClampMax}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMaxDoubleEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxDoubleEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator max;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMaxDoubleEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator max, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) {
+ try (DoubleBlock maxBlock = (DoubleBlock) max.eval(page)) {
+ DoubleVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ DoubleVector maxVector = maxBlock.asVector();
+ if (maxVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, maxVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += max.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock, DoubleBlock maxBlock) {
+ try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (maxBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ double field = fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p));
+ double max = maxBlock.getDouble(maxBlock.getFirstValueIndex(p));
+ result.appendDouble(ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ public DoubleVector eval(int positionCount, DoubleVector fieldVector, DoubleVector maxVector) {
+ try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ double field = fieldVector.getDouble(p);
+ double max = maxVector.getDouble(p);
+ result.appendDouble(p, ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxDoubleEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, max);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory max;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory max) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ }
+
+ @Override
+ public ClampMaxDoubleEvaluator get(DriverContext context) {
+ return new ClampMaxDoubleEvaluator(source, field.get(context), max.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxDoubleEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntEvaluator.java
new file mode 100644
index 0000000000000..96624136cf39d
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+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 ClampMax}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMaxIntEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxIntEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator max;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMaxIntEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator max, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock fieldBlock = (IntBlock) field.eval(page)) {
+ try (IntBlock maxBlock = (IntBlock) max.eval(page)) {
+ IntVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ IntVector maxVector = maxBlock.asVector();
+ if (maxVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, maxVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += max.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public IntBlock eval(int positionCount, IntBlock fieldBlock, IntBlock maxBlock) {
+ try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ if (fieldBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (fieldBlock.getValueCount(p) != 1) {
+ if (fieldBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (maxBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (maxBlock.getValueCount(p) != 1) {
+ if (maxBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ int field = fieldBlock.getInt(fieldBlock.getFirstValueIndex(p));
+ int max = maxBlock.getInt(maxBlock.getFirstValueIndex(p));
+ result.appendInt(ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ public IntVector eval(int positionCount, IntVector fieldVector, IntVector maxVector) {
+ try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ int field = fieldVector.getInt(p);
+ int max = maxVector.getInt(p);
+ result.appendInt(p, ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxIntEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, max);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory max;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory max) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ }
+
+ @Override
+ public ClampMaxIntEvaluator get(DriverContext context) {
+ return new ClampMaxIntEvaluator(source, field.get(context), max.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxIntEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntegerEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntegerEvaluator.java
new file mode 100644
index 0000000000000..7a60af399851d
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntegerEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+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 ClampMax}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMaxIntegerEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxIntegerEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator max;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMaxIntegerEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator max, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock fieldBlock = (IntBlock) field.eval(page)) {
+ try (IntBlock maxBlock = (IntBlock) max.eval(page)) {
+ IntVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ IntVector maxVector = maxBlock.asVector();
+ if (maxVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, maxVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += max.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public IntBlock eval(int positionCount, IntBlock fieldBlock, IntBlock maxBlock) {
+ try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (maxBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ int field = fieldBlock.getInt(fieldBlock.getFirstValueIndex(p));
+ int max = maxBlock.getInt(maxBlock.getFirstValueIndex(p));
+ result.appendInt(ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ public IntVector eval(int positionCount, IntVector fieldVector, IntVector maxVector) {
+ try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ int field = fieldVector.getInt(p);
+ int max = maxVector.getInt(p);
+ result.appendInt(p, ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxIntegerEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, max);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory max;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory max) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ }
+
+ @Override
+ public ClampMaxIntegerEvaluator get(DriverContext context) {
+ return new ClampMaxIntegerEvaluator(source, field.get(context), max.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxIntegerEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxLongEvaluator.java
new file mode 100644
index 0000000000000..21f5b65ef871e
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxLongEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.LongVector;
+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 ClampMax}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMaxLongEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxLongEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator max;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMaxLongEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator max, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock fieldBlock = (LongBlock) field.eval(page)) {
+ try (LongBlock maxBlock = (LongBlock) max.eval(page)) {
+ LongVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ LongVector maxVector = maxBlock.asVector();
+ if (maxVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, maxBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, maxVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += max.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public LongBlock eval(int positionCount, LongBlock fieldBlock, LongBlock maxBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (maxBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ long field = fieldBlock.getLong(fieldBlock.getFirstValueIndex(p));
+ long max = maxBlock.getLong(maxBlock.getFirstValueIndex(p));
+ result.appendLong(ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ public LongVector eval(int positionCount, LongVector fieldVector, LongVector maxVector) {
+ try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ long field = fieldVector.getLong(p);
+ long max = maxVector.getLong(p);
+ result.appendLong(p, ClampMax.process(field, max));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxLongEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, max);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory max;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory max) {
+ this.source = source;
+ this.field = field;
+ this.max = max;
+ }
+
+ @Override
+ public ClampMaxLongEvaluator get(DriverContext context) {
+ return new ClampMaxLongEvaluator(source, field.get(context), max.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMaxLongEvaluator[" + "field=" + field + ", max=" + max + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBooleanEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBooleanEvaluator.java
new file mode 100644
index 0000000000000..ce4fc04004e9a
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBooleanEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BooleanBlock;
+import org.elasticsearch.compute.data.BooleanVector;
+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 ClampMin}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMinBooleanEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinBooleanEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator min;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMinBooleanEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator min, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BooleanBlock fieldBlock = (BooleanBlock) field.eval(page)) {
+ try (BooleanBlock minBlock = (BooleanBlock) min.eval(page)) {
+ BooleanVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ BooleanVector minVector = minBlock.asVector();
+ if (minVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, minVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += min.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public BooleanBlock eval(int positionCount, BooleanBlock fieldBlock, BooleanBlock minBlock) {
+ try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (minBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ boolean field = fieldBlock.getBoolean(fieldBlock.getFirstValueIndex(p));
+ boolean min = minBlock.getBoolean(minBlock.getFirstValueIndex(p));
+ result.appendBoolean(ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ public BooleanVector eval(int positionCount, BooleanVector fieldVector, BooleanVector minVector) {
+ try(BooleanVector.FixedBuilder result = driverContext.blockFactory().newBooleanVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean field = fieldVector.getBoolean(p);
+ boolean min = minVector.getBoolean(p);
+ result.appendBoolean(p, ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinBooleanEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, min);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory min;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory min) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ }
+
+ @Override
+ public ClampMinBooleanEvaluator get(DriverContext context) {
+ return new ClampMinBooleanEvaluator(source, field.get(context), min.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinBooleanEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBytesRefEvaluator.java
new file mode 100644
index 0000000000000..474aedc4306f8
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBytesRefEvaluator.java
@@ -0,0 +1,167 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+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 ClampMin}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMinBytesRefEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinBytesRefEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator min;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMinBytesRefEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator min, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock fieldBlock = (BytesRefBlock) field.eval(page)) {
+ try (BytesRefBlock minBlock = (BytesRefBlock) min.eval(page)) {
+ BytesRefVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ BytesRefVector minVector = minBlock.asVector();
+ if (minVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, minVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += min.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public BytesRefBlock eval(int positionCount, BytesRefBlock fieldBlock, BytesRefBlock minBlock) {
+ try(BytesRefBlock.Builder result = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ BytesRef fieldScratch = new BytesRef();
+ BytesRef minScratch = new BytesRef();
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (minBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ BytesRef field = fieldBlock.getBytesRef(fieldBlock.getFirstValueIndex(p), fieldScratch);
+ BytesRef min = minBlock.getBytesRef(minBlock.getFirstValueIndex(p), minScratch);
+ result.appendBytesRef(ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ public BytesRefVector eval(int positionCount, BytesRefVector fieldVector,
+ BytesRefVector minVector) {
+ try(BytesRefVector.Builder result = driverContext.blockFactory().newBytesRefVectorBuilder(positionCount)) {
+ BytesRef fieldScratch = new BytesRef();
+ BytesRef minScratch = new BytesRef();
+ position: for (int p = 0; p < positionCount; p++) {
+ BytesRef field = fieldVector.getBytesRef(p, fieldScratch);
+ BytesRef min = minVector.getBytesRef(p, minScratch);
+ result.appendBytesRef(ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinBytesRefEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, min);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory min;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory min) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ }
+
+ @Override
+ public ClampMinBytesRefEvaluator get(DriverContext context) {
+ return new ClampMinBytesRefEvaluator(source, field.get(context), min.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinBytesRefEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinDoubleEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinDoubleEvaluator.java
new file mode 100644
index 0000000000000..ea7a91d42e23f
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinDoubleEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.DoubleBlock;
+import org.elasticsearch.compute.data.DoubleVector;
+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 ClampMin}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMinDoubleEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinDoubleEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator min;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMinDoubleEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator min, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) {
+ try (DoubleBlock minBlock = (DoubleBlock) min.eval(page)) {
+ DoubleVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ DoubleVector minVector = minBlock.asVector();
+ if (minVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, minVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += min.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock, DoubleBlock minBlock) {
+ try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (minBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ double field = fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p));
+ double min = minBlock.getDouble(minBlock.getFirstValueIndex(p));
+ result.appendDouble(ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ public DoubleVector eval(int positionCount, DoubleVector fieldVector, DoubleVector minVector) {
+ try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ double field = fieldVector.getDouble(p);
+ double min = minVector.getDouble(p);
+ result.appendDouble(p, ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinDoubleEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, min);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory min;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory min) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ }
+
+ @Override
+ public ClampMinDoubleEvaluator get(DriverContext context) {
+ return new ClampMinDoubleEvaluator(source, field.get(context), min.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinDoubleEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntEvaluator.java
new file mode 100644
index 0000000000000..d650458e2d3d6
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+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 ClampMin}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMinIntEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinIntEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator min;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMinIntEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator min, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock fieldBlock = (IntBlock) field.eval(page)) {
+ try (IntBlock minBlock = (IntBlock) min.eval(page)) {
+ IntVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ IntVector minVector = minBlock.asVector();
+ if (minVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, minVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += min.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public IntBlock eval(int positionCount, IntBlock fieldBlock, IntBlock minBlock) {
+ try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ if (fieldBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (fieldBlock.getValueCount(p) != 1) {
+ if (fieldBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (minBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (minBlock.getValueCount(p) != 1) {
+ if (minBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ int field = fieldBlock.getInt(fieldBlock.getFirstValueIndex(p));
+ int min = minBlock.getInt(minBlock.getFirstValueIndex(p));
+ result.appendInt(ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ public IntVector eval(int positionCount, IntVector fieldVector, IntVector minVector) {
+ try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ int field = fieldVector.getInt(p);
+ int min = minVector.getInt(p);
+ result.appendInt(p, ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinIntEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, min);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory min;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory min) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ }
+
+ @Override
+ public ClampMinIntEvaluator get(DriverContext context) {
+ return new ClampMinIntEvaluator(source, field.get(context), min.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinIntEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntegerEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntegerEvaluator.java
new file mode 100644
index 0000000000000..a8ec2068bd571
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntegerEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+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 ClampMin}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMinIntegerEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinIntegerEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator min;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMinIntegerEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator min, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock fieldBlock = (IntBlock) field.eval(page)) {
+ try (IntBlock minBlock = (IntBlock) min.eval(page)) {
+ IntVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ IntVector minVector = minBlock.asVector();
+ if (minVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, minVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += min.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public IntBlock eval(int positionCount, IntBlock fieldBlock, IntBlock minBlock) {
+ try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (minBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ int field = fieldBlock.getInt(fieldBlock.getFirstValueIndex(p));
+ int min = minBlock.getInt(minBlock.getFirstValueIndex(p));
+ result.appendInt(ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ public IntVector eval(int positionCount, IntVector fieldVector, IntVector minVector) {
+ try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ int field = fieldVector.getInt(p);
+ int min = minVector.getInt(p);
+ result.appendInt(p, ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinIntegerEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, min);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory min;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory min) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ }
+
+ @Override
+ public ClampMinIntegerEvaluator get(DriverContext context) {
+ return new ClampMinIntegerEvaluator(source, field.get(context), min.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinIntegerEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinLongEvaluator.java
new file mode 100644
index 0000000000000..39912eaf269fb
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinLongEvaluator.java
@@ -0,0 +1,161 @@
+// 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.conditional;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.RamUsageEstimator;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.LongVector;
+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 ClampMin}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class ClampMinLongEvaluator implements EvalOperator.ExpressionEvaluator {
+ private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinLongEvaluator.class);
+
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator field;
+
+ private final EvalOperator.ExpressionEvaluator min;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public ClampMinLongEvaluator(Source source, EvalOperator.ExpressionEvaluator field,
+ EvalOperator.ExpressionEvaluator min, DriverContext driverContext) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock fieldBlock = (LongBlock) field.eval(page)) {
+ try (LongBlock minBlock = (LongBlock) min.eval(page)) {
+ LongVector fieldVector = fieldBlock.asVector();
+ if (fieldVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ LongVector minVector = minBlock.asVector();
+ if (minVector == null) {
+ return eval(page.getPositionCount(), fieldBlock, minBlock);
+ }
+ return eval(page.getPositionCount(), fieldVector, minVector).asBlock();
+ }
+ }
+ }
+
+ @Override
+ public long baseRamBytesUsed() {
+ long baseRamBytesUsed = BASE_RAM_BYTES_USED;
+ baseRamBytesUsed += field.baseRamBytesUsed();
+ baseRamBytesUsed += min.baseRamBytesUsed();
+ return baseRamBytesUsed;
+ }
+
+ public LongBlock eval(int positionCount, LongBlock fieldBlock, LongBlock minBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ switch (fieldBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ switch (minBlock.getValueCount(p)) {
+ case 0:
+ result.appendNull();
+ continue position;
+ case 1:
+ break;
+ default:
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ result.appendNull();
+ continue position;
+ }
+ long field = fieldBlock.getLong(fieldBlock.getFirstValueIndex(p));
+ long min = minBlock.getLong(minBlock.getFirstValueIndex(p));
+ result.appendLong(ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ public LongVector eval(int positionCount, LongVector fieldVector, LongVector minVector) {
+ try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ long field = fieldVector.getLong(p);
+ long min = minVector.getLong(p);
+ result.appendLong(p, ClampMin.process(field, min));
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinLongEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(field, min);
+ }
+
+ 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 field;
+
+ private final EvalOperator.ExpressionEvaluator.Factory min;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field,
+ EvalOperator.ExpressionEvaluator.Factory min) {
+ this.source = source;
+ this.field = field;
+ this.min = min;
+ }
+
+ @Override
+ public ClampMinLongEvaluator get(DriverContext context) {
+ return new ClampMinLongEvaluator(source, field.get(context), min.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "ClampMinLongEvaluator[" + "field=" + field + ", min=" + min + "]";
+ }
+ }
+}
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 e80ebee86ffc6..b431acd0cd03b 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
@@ -1135,6 +1135,7 @@ public enum Cap {
*/
INCREASE,
DELTA_TS_AGG,
+ CLAMP_FUNCTIONS,
/**
* Resolve groupings before resolving references to groupings in the aggregations.
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 3c8ffb45585db..c6520c8563d6d 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
@@ -64,7 +64,10 @@
import org.elasticsearch.xpack.esql.expression.function.grouping.Categorize;
import org.elasticsearch.xpack.esql.expression.function.grouping.TBucket;
import org.elasticsearch.xpack.esql.expression.function.inference.TextEmbedding;
+import org.elasticsearch.xpack.esql.expression.function.scalar.Clamp;
import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Case;
+import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMax;
+import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMin;
import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Greatest;
import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Least;
import org.elasticsearch.xpack.esql.expression.function.scalar.convert.FromBase64;
@@ -341,6 +344,7 @@ private static FunctionDefinition[][] functions() {
// use casting to disambiguate between the two
new FunctionDefinition[] {
def(Avg.class, uni(Avg::new), "avg"),
+ def(Clamp.class, tri(Clamp::new), "clamp"),
def(Count.class, uni(Count::new), "count"),
def(CountDistinct.class, bi(CountDistinct::new), "count_distinct"),
def(Max.class, uni(Max::new), "max"),
@@ -376,6 +380,8 @@ private static FunctionDefinition[][] functions() {
def(Log.class, Log::new, "log"),
def(Log10.class, Log10::new, "log10"),
def(Least.class, Least::new, "least"),
+ def(ClampMax.class, ClampMax::new, "clamp_max"),
+ def(ClampMin.class, ClampMin::new, "clamp_min"),
def(Pi.class, Pi::new, "pi"),
def(Pow.class, Pow::new, "pow"),
def(Round.class, Round::new, "round"),
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/Clamp.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/Clamp.java
new file mode 100644
index 0000000000000..21c3271163202
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/Clamp.java
@@ -0,0 +1,138 @@
+/*
+ * 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;
+
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.expression.TypeResolutions;
+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.SurrogateExpression;
+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.conditional.ClampMax;
+import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMin;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.NULL;
+
+/**
+ * Clamps the values of all samples to have a lower limit of min and an upper limit of max.
+ */
+public class Clamp extends EsqlScalarFunction implements SurrogateExpression {
+ private final Expression field;
+ private final Expression min;
+ private final Expression max;
+
+ @FunctionInfo(
+ returnType = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "Clamps the values of all samples to have a lower limit of min and an upper limit of max.",
+ examples = { @Example(file = "k8s-timeseries-clamp", tag = "clamp") }
+ )
+ public Clamp(
+ Source source,
+ @Param(
+ name = "field",
+ type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "Numeric expression. If `null`, the function returns `null`."
+ ) Expression field,
+ @Param(
+ name = "min",
+ type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "The min value to clamp data into."
+ ) Expression min,
+ @Param(
+ name = "max",
+ type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "The max value to clamp data into."
+ ) Expression max
+ ) {
+ super(source, List.of(field, min, max));
+ this.field = field;
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ public String getWriteableName() {
+ throw new UnsupportedOperationException("Clamp does not support serialization.");
+ }
+
+ @Override
+ protected TypeResolution resolveType() {
+ if (childrenResolved() == false) {
+ return new TypeResolution("Unresolved children");
+ }
+
+ var field = children().get(0);
+ var fieldDataType = field.dataType().noText();
+ TypeResolution resolution = TypeResolutions.isType(
+ field,
+ t -> t.isNumeric() || t == DataType.BOOLEAN || t.isDate() || DataType.isString(t) || t == DataType.IP || t == DataType.VERSION,
+ sourceText(),
+ TypeResolutions.ParamOrdinal.FIRST,
+ fieldDataType.typeName()
+ );
+ if (resolution.unresolved()) {
+ return resolution;
+ }
+ if (fieldDataType == NULL) {
+ return new TypeResolution("'field' must not be null in clamp()");
+ }
+ for (Expression child : List.of(children().get(1), children().get(2))) {
+ var childRes = TypeResolutions.isType(
+ child,
+ t -> t.isNumeric() ? fieldDataType.isNumeric() : t.noText() == fieldDataType,
+ sourceText(),
+ child == children().get(1) ? TypeResolutions.ParamOrdinal.SECOND : TypeResolutions.ParamOrdinal.THIRD,
+ fieldDataType.typeName()
+ );
+ if (childRes.unresolved()) {
+ return childRes;
+ }
+ }
+ return TypeResolution.TYPE_RESOLVED;
+ }
+
+ @Override
+ public DataType dataType() {
+ return field.dataType();
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new Clamp(source(), newChildren.get(0), newChildren.get(1), newChildren.get(2));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, Clamp::new, field, children().get(1), children().get(2));
+ }
+
+ @Override
+ public void writeTo(StreamOutput out) throws IOException {
+ throw new UnsupportedOperationException("Clamp does not support serialization.");
+ }
+
+ @Override
+ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
+ throw new UnsupportedOperationException(
+ "Clamp should have been replaced by ClampMin and ClampMax. Something went wrong in the compute engine."
+ );
+ }
+
+ @Override
+ public Expression surrogate() {
+ return new ClampMax(source(), new ClampMin(source(), field, min), max);
+ }
+}
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 978f0218a798f..961d577692aa0 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
@@ -10,6 +10,8 @@
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.xpack.esql.expression.function.grouping.GroupingWritables;
import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Case;
+import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMax;
+import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMin;
import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Greatest;
import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Least;
import org.elasticsearch.xpack.esql.expression.function.scalar.convert.FromAggregateMetricDouble;
@@ -90,6 +92,8 @@ public static List getNamedWriteables() {
entries.add(MonthName.ENTRY);
entries.add(IpPrefix.ENTRY);
entries.add(Least.ENTRY);
+ entries.add(ClampMax.ENTRY);
+ entries.add(ClampMin.ENTRY);
entries.add(Left.ENTRY);
entries.add(Locate.ENTRY);
entries.add(Log.ENTRY);
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMax.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMax.java
new file mode 100644
index 0000000000000..1d9c7b2c703d8
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMax.java
@@ -0,0 +1,186 @@
+/*
+ * 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.conditional;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.compute.ann.Evaluator;
+import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator;
+import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.expression.Expressions;
+import org.elasticsearch.xpack.esql.core.expression.TypeResolutions;
+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;
+import org.elasticsearch.xpack.esql.expression.function.scalar.math.Cast;
+import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput;
+import org.elasticsearch.xpack.esql.planner.PlannerUtils;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.NULL;
+
+/**
+ * Clamps the input values to have an upper limit of max.
+ */
+public class ClampMax extends EsqlScalarFunction {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "ClampMax", ClampMax::new);
+
+ @FunctionInfo(
+ returnType = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "Returns clamps the values of all input samples clamped to have an upper limit of max.",
+ examples = @Example(file = "k8s-timeseries-clamp", tag = "clamp-max")
+ )
+ public ClampMax(
+ Source source,
+ @Param(
+ name = "field",
+ type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "field to clamp."
+ ) Expression field,
+ @Param(
+ name = "max",
+ type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "The max value to clamp data into."
+ ) Expression max
+ ) {
+ super(source, List.of(field, max));
+ }
+
+ private ClampMax(StreamInput in) throws IOException {
+ this(Source.readFrom((PlanStreamInput) in), in.readNamedWriteable(Expression.class), in.readNamedWriteable(Expression.class));
+ }
+
+ @Override
+ public void writeTo(StreamOutput out) throws IOException {
+ source().writeTo(out);
+ out.writeNamedWriteable(children().get(0));
+ out.writeNamedWriteable(children().get(1));
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ public DataType dataType() {
+ return children().getFirst().dataType();
+ }
+
+ @Override
+ protected TypeResolution resolveType() {
+ if (childrenResolved() == false) {
+ return new TypeResolution("Unresolved children");
+ }
+
+ var field = children().get(0);
+ var min = children().get(1);
+ var fieldDataType = field.dataType();
+ TypeResolution resolution = TypeResolutions.isType(
+ field,
+ t -> t.isNumeric() || t == DataType.BOOLEAN || t.isDate() || DataType.isString(t) || t == DataType.IP || t == DataType.VERSION,
+ sourceText(),
+ TypeResolutions.ParamOrdinal.FIRST,
+ fieldDataType.typeName()
+ );
+ if (resolution.unresolved()) {
+ return resolution;
+ }
+ if (fieldDataType == NULL) {
+ return new TypeResolution("'field' must not be null in clamp()");
+ }
+ resolution = TypeResolutions.isType(
+ min,
+ t -> t.isNumeric() ? fieldDataType.isNumeric() : t.noText() == fieldDataType,
+ sourceText(),
+ TypeResolutions.ParamOrdinal.SECOND,
+ fieldDataType.typeName()
+ );
+ if (resolution.unresolved()) {
+ return resolution;
+ }
+ return TypeResolution.TYPE_RESOLVED;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new ClampMax(source(), newChildren.get(0), newChildren.get(1));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, ClampMax::new, children().get(0), children().get(1));
+ }
+
+ @Override
+ public boolean foldable() {
+ return Expressions.foldable(children());
+ }
+
+ @Override
+ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
+ // force datatype initialization
+ var outputType = dataType();
+
+ var max = children().get(1);
+ var maxF = outputType != max.dataType()
+ ? Cast.cast(source(), max.dataType(), outputType, toEvaluator.apply(max))
+ : toEvaluator.apply(max);
+
+ return switch (PlannerUtils.toElementType(outputType)) {
+ case BOOLEAN -> new ClampMaxBooleanEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF);
+ case DOUBLE -> new ClampMaxDoubleEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF);
+ case INT -> new ClampMaxIntegerEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF);
+ case LONG -> new ClampMaxLongEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF);
+ case BYTES_REF -> new ClampMaxBytesRefEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF);
+ default -> throw EsqlIllegalArgumentException.illegalDataType(outputType);
+ };
+ }
+
+ @Evaluator(extraName = "Boolean")
+ static boolean process(boolean field, boolean max) {
+ if (max == false) {
+ return false;
+ } else {
+ return field;
+ }
+ }
+
+ @Evaluator(extraName = "BytesRef")
+ static BytesRef process(BytesRef field, BytesRef max) {
+ if (field.compareTo(max) > 0) {
+ return max;
+ } else {
+ return field;
+ }
+ }
+
+ @Evaluator(extraName = "Integer")
+ static int process(int field, int max) {
+ return Math.min(field, max);
+ }
+
+ @Evaluator(extraName = "Long")
+ static long process(long field, long max) {
+ return Math.min(field, max);
+ }
+
+ @Evaluator(extraName = "Double")
+ static double process(double field, double max) {
+ return Math.min(field, max);
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMin.java
new file mode 100644
index 0000000000000..0ecdddb678059
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMin.java
@@ -0,0 +1,182 @@
+/*
+ * 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.conditional;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.compute.ann.Evaluator;
+import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator;
+import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.expression.Expressions;
+import org.elasticsearch.xpack.esql.core.expression.TypeResolutions;
+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;
+import org.elasticsearch.xpack.esql.expression.function.scalar.math.Cast;
+import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput;
+import org.elasticsearch.xpack.esql.planner.PlannerUtils;
+
+import java.io.IOException;
+import java.util.List;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.NULL;
+
+/**
+ * Clamps input values to have a lower limit of min.
+ */
+public class ClampMin extends EsqlScalarFunction {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "ClampMin", ClampMin::new);
+
+ @FunctionInfo(
+ returnType = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "Returns clamps the values of all input samples clamped to have a lower limit of min.",
+ examples = @Example(file = "k8s-timeseries-clamp", tag = "clamp-min")
+ )
+ public ClampMin(
+ Source source,
+ @Param(
+ name = "field",
+ type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "field to clamp."
+ ) Expression field,
+ @Param(
+ name = "min",
+ type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" },
+ description = "The min value to clamp data into."
+ ) Expression min
+ ) {
+ super(source, List.of(field, min));
+ }
+
+ private ClampMin(StreamInput in) throws IOException {
+ this(Source.readFrom((PlanStreamInput) in), in.readNamedWriteable(Expression.class), in.readNamedWriteable(Expression.class));
+ }
+
+ @Override
+ public void writeTo(StreamOutput out) throws IOException {
+ source().writeTo(out);
+ out.writeNamedWriteable(children().get(0));
+ out.writeNamedWriteable(children().get(1));
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ public DataType dataType() {
+ return children().getFirst().dataType();
+ }
+
+ @Override
+ protected TypeResolution resolveType() {
+ if (childrenResolved() == false) {
+ return new TypeResolution("Unresolved children");
+ }
+
+ var field = children().get(0);
+ var min = children().get(1);
+ var fieldDataType = field.dataType();
+ TypeResolution resolution = TypeResolutions.isType(
+ field,
+ t -> t.isNumeric() || t == DataType.BOOLEAN || t.isDate() || DataType.isString(t) || t == DataType.IP || t == DataType.VERSION,
+ sourceText(),
+ TypeResolutions.ParamOrdinal.FIRST,
+ fieldDataType.typeName()
+ );
+ if (resolution.unresolved()) {
+ return resolution;
+ }
+ if (fieldDataType == NULL) {
+ return new TypeResolution("'field' must not be null in clamp()");
+ }
+ resolution = TypeResolutions.isType(
+ min,
+ t -> t.isNumeric() ? fieldDataType.isNumeric() : t.noText() == fieldDataType,
+ sourceText(),
+ TypeResolutions.ParamOrdinal.SECOND,
+ fieldDataType.typeName()
+ );
+ if (resolution.unresolved()) {
+ return resolution;
+ }
+ return TypeResolution.TYPE_RESOLVED;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new ClampMin(source(), newChildren.get(0), newChildren.get(1));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, ClampMin::new, children().get(0), children().get(1));
+ }
+
+ @Override
+ public boolean foldable() {
+ return Expressions.foldable(children());
+ }
+
+ @Override
+ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
+ // force datatype initialization
+ var outputType = dataType();
+
+ var min = children().get(1);
+ var minF = outputType != min.dataType()
+ ? Cast.cast(source(), min.dataType(), outputType, toEvaluator.apply(min))
+ : toEvaluator.apply(min);
+
+ return switch (PlannerUtils.toElementType(outputType)) {
+ case BOOLEAN -> new ClampMinBooleanEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF);
+ case DOUBLE -> new ClampMinDoubleEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF);
+ case INT -> new ClampMinIntegerEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF);
+ case LONG -> new ClampMinLongEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF);
+ case BYTES_REF -> new ClampMinBytesRefEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF);
+ default -> throw EsqlIllegalArgumentException.illegalDataType(outputType);
+ };
+ }
+
+ @Evaluator(extraName = "Boolean")
+ static boolean process(boolean field, boolean min) {
+ if (min) {
+ return true;
+ } else {
+ return field;
+ }
+ }
+
+ @Evaluator(extraName = "BytesRef")
+ static BytesRef process(BytesRef field, BytesRef min) {
+ return field.compareTo(min) < 0 ? min : field;
+ }
+
+ @Evaluator(extraName = "Integer")
+ static int process(int field, int min) {
+ return Math.max(field, min);
+ }
+
+ @Evaluator(extraName = "Long")
+ static long process(long field, long min) {
+ return Math.max(field, min);
+ }
+
+ @Evaluator(extraName = "Double")
+ static double process(double field, double min) {
+ return Math.max(field, min);
+ }
+}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxTests.java
new file mode 100644
index 0000000000000..c331cc719686f
--- /dev/null
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxTests.java
@@ -0,0 +1,191 @@
+/*
+ * 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.conditional;
+
+import com.carrotsearch.randomizedtesting.annotations.Name;
+import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.core.Tuple;
+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 org.elasticsearch.xpack.esql.expression.function.scalar.EsqlScalarFunction;
+import org.hamcrest.Matchers;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class ClampMaxTests extends AbstractScalarFunctionTestCase {
+ public ClampMaxTests(@Name("TestCase") Supplier testCaseSupplier) {
+ this.testCase = testCaseSupplier.get();
+ }
+
+ @ParametersFactory
+ public static Iterable