Skip to content

Commit f16f71e

Browse files
authored
ESQL: Add ip_prefix function (#109070)
Added ESQL function to get the prefix of an IP. It works now with both IPv4 and IPv6. For users planning to use it with mixed IPs, we may need to add a function like "is_ipv4()" first. **About the skipped test:** There's currently a "bug" in the evaluators//functions that return null. Evaluators can't handle them. We'll work on support for that in another PR. It affects other functions, like `substring()`. In this function, however, it only affects in "wrong" cases (Like an invalid prefix), so it has no impact. Fixes #99064
1 parent da9282e commit f16f71e

File tree

20 files changed

+871
-2
lines changed

20 files changed

+871
-2
lines changed

.gitattributes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@ x-pack/plugin/esql/src/main/antlr/*.tokens linguist-generated=true
88
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/*.interp linguist-generated=true
99
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseLexer*.java linguist-generated=true
1010
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/EsqlBaseParser*.java linguist-generated=true
11+
x-pack/plugin/esql/src/main/generated/** linguist-generated=true
12+
13+
# ESQL functions docs are autogenerated. More information at `docs/reference/esql/functions/README.md`
14+
docs/reference/esql/functions/*/** linguist-generated=true
15+

docs/changelog/109070.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 109070
2+
summary: "ESQL: Add `ip_prefix` function"
3+
area: ES|QL
4+
type: feature
5+
issues:
6+
- 99064

docs/reference/esql/functions/description/ip_prefix.asciidoc

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/esql/functions/examples/ip_prefix.asciidoc

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/esql/functions/kibana/definition/ip_prefix.json

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/esql/functions/kibana/docs/ip_prefix.md

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/esql/functions/layout/ip_prefix.asciidoc

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/esql/functions/parameters/ip_prefix.asciidoc

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/esql/functions/signature/ip_prefix.svg

Lines changed: 1 addition & 0 deletions
Loading

docs/reference/esql/functions/types/ip_prefix.asciidoc

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public static Tuple<Version, Version> skipVersionRange(String testName) {
101101
Map<String, String> pairs = extractInstructions(testName);
102102
String versionRange = pairs.get("skip");
103103
if (versionRange != null) {
104-
String[] skipVersions = versionRange.split("-");
104+
String[] skipVersions = versionRange.split("-", Integer.MAX_VALUE);
105105
if (skipVersions.length != 2) {
106106
throw new IllegalArgumentException("malformed version range : " + versionRange);
107107
}

x-pack/plugin/esql/qa/testFixtures/src/main/resources/ip.csv-spec

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,3 +485,108 @@ beta | 127.0.0.1
485485
beta | 127.0.0.1
486486
beta | 127.0.0.1
487487
;
488+
489+
ipPrefix
490+
required_capability: fn_ip_prefix
491+
//tag::ipPrefix[]
492+
row ip4 = to_ip("1.2.3.4"), ip6 = to_ip("fe80::cae2:65ff:fece:feb9")
493+
| eval ip4_prefix = ip_prefix(ip4, 24, 0), ip6_prefix = ip_prefix(ip6, 0, 112);
494+
//end::ipPrefix[]
495+
496+
//tag::ipPrefix-result[]
497+
ip4:ip | ip6:ip | ip4_prefix:ip | ip6_prefix:ip
498+
1.2.3.4 | fe80::cae2:65ff:fece:feb9 | 1.2.3.0 | fe80::cae2:65ff:fece:0000
499+
//end::ipPrefix-result[]
500+
;
501+
502+
ipPrefixCompleteIp
503+
required_capability: fn_ip_prefix
504+
row ip4 = to_ip("1.2.3.4"), ip6 = to_ip("fe80::cae2:65ff:fece:feb9")
505+
| eval ip4_prefix = ip_prefix(ip4, 32, 0), ip6_prefix = ip_prefix(ip6, 0, 128);
506+
507+
ip4:ip | ip6:ip | ip4_prefix:ip | ip6_prefix:ip
508+
1.2.3.4 | fe80::cae2:65ff:fece:feb9 | 1.2.3.4 | fe80::cae2:65ff:fece:feb9
509+
;
510+
511+
ipPrefixZeroBits
512+
required_capability: fn_ip_prefix
513+
row ip4 = to_ip("1.2.3.4"), ip6 = to_ip("fe80::cae2:65ff:fece:feb9")
514+
| eval ip4_prefix = ip_prefix(ip4, 0, 128), ip6_prefix = ip_prefix(ip6, 32, 0);
515+
516+
ip4:ip | ip6:ip | ip4_prefix:ip | ip6_prefix:ip
517+
1.2.3.4 | fe80::cae2:65ff:fece:feb9 | 0.0.0.0 | ::0
518+
;
519+
520+
ipPrefixWithBits
521+
required_capability: fn_ip_prefix
522+
row ip4 = to_ip("1.2.3.255"), ip6 = to_ip("fe80::cae2:65ff:fece:feff")
523+
| eval ip4_prefix = ip_prefix(ip4, 25, 0), ip6_prefix = ip_prefix(ip6, 0, 121);
524+
525+
ip4:ip | ip6:ip | ip4_prefix:ip | ip6_prefix:ip
526+
1.2.3.255 | fe80::cae2:65ff:fece:feff | 1.2.3.128 | fe80::cae2:65ff:fece:fe80
527+
;
528+
529+
ipPrefixLengthFromColumn
530+
required_capability: fn_ip_prefix
531+
from hosts
532+
| where host == "alpha"
533+
| sort card
534+
| eval prefix = ip_prefix(ip0, 24, 128)
535+
| keep card, ip0, prefix;
536+
537+
card:keyword | ip0:ip | prefix:ip
538+
eth0 | 127.0.0.1 | 127.0.0.0
539+
eth1 | ::1 | ::0
540+
;
541+
542+
ipPrefixLengthFromExpression
543+
required_capability: fn_ip_prefix
544+
row ip4 = to_ip("1.2.3.4"), ip6 = to_ip("fe80::cae2:65ff:fece:feb9"), bits_per_byte = 8
545+
| eval ip4_length = 3 * bits_per_byte, ip4_prefix = ip_prefix(ip4, ip4_length, 0), ip6_prefix = ip_prefix(ip6, 0, 12 * 10);
546+
547+
ip4:ip | ip6:ip | bits_per_byte:integer | ip4_length:integer | ip4_prefix:ip | ip6_prefix:ip
548+
1.2.3.4 | fe80::cae2:65ff:fece:feb9 | 8 | 24 | 1.2.3.0 | fe80::cae2:65ff:fece:fe00
549+
;
550+
551+
ipPrefixAsGroup
552+
required_capability: fn_ip_prefix
553+
from hosts
554+
| stats count(*) by ip_prefix(ip1, 24, 120)
555+
| sort `ip_prefix(ip1, 24, 120)`;
556+
warning:Line 2:21: evaluation of [ip_prefix(ip1, 24, 120)] failed, treating result as null. Only first 20 failures recorded.
557+
warning:Line 2:21: java.lang.IllegalArgumentException: single-value function encountered multi-value
558+
559+
count(*):long | ip_prefix(ip1, 24, 120):ip
560+
2 | ::0
561+
3 | 127.0.0.0
562+
1 | 128.0.0.0
563+
1 | fe80::cae2:65ff:fece:fe00
564+
1 | fe81::cae2:65ff:fece:fe00
565+
2 | null
566+
;
567+
568+
ipPrefixWithWrongLengths
569+
required_capability: fn_ip_prefix
570+
row ip4 = to_ip("1.2.3.4")
571+
| eval a = ip_prefix(ip4, -1, 128), b = ip_prefix(ip4, 32, -1), c = ip_prefix(ip4, 33, 0), d = ip_prefix(ip4, 32, 129);
572+
warning:Line 2:12: evaluation of [ip_prefix(ip4, -1, 128)] failed, treating result as null. Only first 20 failures recorded.
573+
warning:Line 2:12: java.lang.IllegalArgumentException: Prefix length v4 must be in range [0, 32], found -1
574+
warning:Line 2:41: evaluation of [ip_prefix(ip4, 32, -1)] failed, treating result as null. Only first 20 failures recorded.
575+
warning:Line 2:41: java.lang.IllegalArgumentException: Prefix length v6 must be in range [0, 128], found -1
576+
warning:Line 2:69: evaluation of [ip_prefix(ip4, 33, 0)] failed, treating result as null. Only first 20 failures recorded.
577+
warning:Line 2:69: java.lang.IllegalArgumentException: Prefix length v4 must be in range [0, 32], found 33
578+
warning:Line 2:96: evaluation of [ip_prefix(ip4, 32, 129)] failed, treating result as null. Only first 20 failures recorded.
579+
warning:Line 2:96: java.lang.IllegalArgumentException: Prefix length v6 must be in range [0, 128], found 129
580+
581+
ip4:ip | a:ip | b:ip | c:ip | d:ip
582+
1.2.3.4 | null | null | null | null
583+
;
584+
585+
ipPrefixWithNullArguments
586+
required_capability: fn_ip_prefix
587+
row ip4 = to_ip("1.2.3.4")
588+
| eval a = ip_prefix(null, 32, 128), b = ip_prefix(ip4, null, 128), c = ip_prefix(ip4, 32, null);
589+
590+
ip4:ip | a:ip | b:ip | c:ip
591+
1.2.3.4 | null | null | null
592+
;

x-pack/plugin/esql/qa/testFixtures/src/main/resources/meta.csv-spec

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ double e()
3030
"double|integer|long|unsigned_long floor(number:double|integer|long|unsigned_long)"
3131
"keyword from_base64(string:keyword|text)"
3232
"boolean|double|integer|ip|keyword|long|text|version greatest(first:boolean|double|integer|ip|keyword|long|text|version, ?rest...:boolean|double|integer|ip|keyword|long|text|version)"
33+
"ip ip_prefix(ip:ip, prefixLengthV4:integer, prefixLengthV6:integer)"
3334
"boolean|double|integer|ip|keyword|long|text|version least(first:boolean|double|integer|ip|keyword|long|text|version, ?rest...:boolean|double|integer|ip|keyword|long|text|version)"
3435
"keyword left(string:keyword|text, length:integer)"
3536
"integer length(string:keyword|text)"
@@ -144,6 +145,7 @@ ends_with |[str, suffix] |["keyword|text", "keyword|te
144145
floor |number |"double|integer|long|unsigned_long" |Numeric expression. If `null`, the function returns `null`.
145146
from_base64 |string |"keyword|text" |A base64 string.
146147
greatest |first |"boolean|double|integer|ip|keyword|long|text|version" |First of the columns to evaluate.
148+
ip_prefix |[ip, prefixLengthV4, prefixLengthV6]|[ip, integer, integer] |[IP address of type `ip` (both IPv4 and IPv6 are supported)., Prefix length for IPv4 addresses., Prefix length for IPv6 addresses.]
147149
least |first |"boolean|double|integer|ip|keyword|long|text|version" |First of the columns to evaluate.
148150
left |[string, length] |["keyword|text", integer] |[The string from which to return a substring., The number of characters to return.]
149151
length |string |"keyword|text" |String expression. If `null`, the function returns `null`.
@@ -259,6 +261,7 @@ ends_with |Returns a boolean that indicates whether a keyword string ends wi
259261
floor |Round a number down to the nearest integer.
260262
from_base64 |Decode a base64 string.
261263
greatest |Returns the maximum value from multiple columns. This is similar to <<esql-mv_max>> except it is intended to run on multiple columns at once.
264+
ip_prefix |Truncates an IP to a given prefix length.
262265
least |Returns the minimum value from multiple columns. This is similar to <<esql-mv_min>> except it is intended to run on multiple columns at once.
263266
left |Returns the substring that extracts 'length' chars from 'string' starting from the left.
264267
length |Returns the character length of a string.
@@ -375,6 +378,7 @@ ends_with |boolean
375378
floor |"double|integer|long|unsigned_long" |false |false |false
376379
from_base64 |keyword |false |false |false
377380
greatest |"boolean|double|integer|ip|keyword|long|text|version" |false |true |false
381+
ip_prefix |ip |[false, false, false] |false |false
378382
least |"boolean|double|integer|ip|keyword|long|text|version" |false |true |false
379383
left |keyword |[false, false] |false |false
380384
length |integer |false |false |false
@@ -471,5 +475,5 @@ countFunctions#[skip:-8.14.99, reason:BIN added]
471475
meta functions | stats a = count(*), b = count(*), c = count(*) | mv_expand c;
472476

473477
a:long | b:long | c:long
474-
106 | 106 | 106
478+
107 | 107 | 107
475479
;

0 commit comments

Comments
 (0)