diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java index 30f2edc53f17c..84bb213fb6805 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ExpressionBuilder.java @@ -388,6 +388,8 @@ public DataType visitPrimitiveDataType(PrimitiveDataTypeContext ctx) { case "varchar": case "string": return DataType.KEYWORD; + case "ip": + return DataType.IP; default: throw new ParsingException(source(ctx), "Does not recognize type {}", type); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java index 605cb11beba9d..3312f449ec622 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.type; import org.elasticsearch.common.Booleans; +import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -117,6 +118,8 @@ private static Conversion conversion(DataType from, DataType to) { case KEYWORD: case TEXT: return conversionToString(from); + case IP: + return conversionToIp(from); case LONG: return conversionToLong(from); case INTEGER: @@ -146,6 +149,13 @@ private static Conversion conversionToString(DataType from) { return Conversion.OTHER_TO_STRING; } + private static Conversion conversionToIp(DataType from) { + if (from.isString()) { + return Conversion.STRING_TO_IP; + } + return null; + } + private static Conversion conversionToLong(DataType from) { if (from.isRational) { return Conversion.RATIONAL_TO_LONG; @@ -409,7 +419,14 @@ public enum Conversion { STRING_TO_BOOLEAN(fromString(DataTypeConversion::convertToBoolean, "Boolean")), DATE_TO_BOOLEAN(fromDate(value -> value != 0)), - BOOL_TO_LONG(fromBool(value -> value ? 1L : 0L)); + BOOL_TO_LONG(fromBool(value -> value ? 1L : 0L)), + + STRING_TO_IP(o -> { + if (!InetAddresses.isInetAddress(o.toString())) { + throw new SqlIllegalArgumentException( "[" + o + "] is not a valid IPv4 or IPv6 address"); + } + return o; + }); private final Function converter; @@ -464,4 +481,4 @@ public static DataType asInteger(DataType dataType) { return dataType.isInteger ? dataType : LONG; } -} \ No newline at end of file +} diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java index f773634fe721f..b191646a9cdee 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java @@ -7,10 +7,13 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; +import org.elasticsearch.xpack.sql.expression.Literal; import org.elasticsearch.xpack.sql.type.DataTypeConversion.Conversion; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import static org.elasticsearch.xpack.sql.tree.Location.EMPTY; + public class DataTypeConversionTests extends ESTestCase { public void testConversionToString() { Conversion conversion = DataTypeConversion.conversionFor(DataType.DOUBLE, DataType.KEYWORD); @@ -252,4 +255,19 @@ public void testConversionToUnsupported() { () -> DataTypeConversion.conversionFor(DataType.INTEGER, DataType.UNSUPPORTED)); assertEquals("cannot convert from [INTEGER] to [UNSUPPORTED]", e.getMessage()); } + + public void testStringToIp() { + Conversion conversion = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.IP); + assertNull(conversion.convert(null)); + assertEquals("192.168.1.1", conversion.convert("192.168.1.1")); + Exception e = expectThrows(SqlIllegalArgumentException.class, () -> conversion.convert("10.1.1.300")); + assertEquals("[10.1.1.300] is not a valid IPv4 or IPv6 address", e.getMessage()); + } + + public void testIpToString() { + Conversion ipToString = DataTypeConversion.conversionFor(DataType.IP, DataType.KEYWORD); + assertEquals("10.0.0.1", ipToString.convert(new Literal(EMPTY, "10.0.0.1", DataType.IP))); + Conversion stringToIp = DataTypeConversion.conversionFor(DataType.KEYWORD, DataType.IP); + assertEquals("10.0.0.1", ipToString.convert(stringToIp.convert(Literal.of(EMPTY, "10.0.0.1")))); + } } diff --git a/x-pack/qa/sql/src/main/resources/ip.csv-spec b/x-pack/qa/sql/src/main/resources/ip.csv-spec index e8075d57c05c6..e6eb81ff8bf45 100644 --- a/x-pack/qa/sql/src/main/resources/ip.csv-spec +++ b/x-pack/qa/sql/src/main/resources/ip.csv-spec @@ -72,6 +72,26 @@ SELECT id, client_ip, dest_ip FROM logs WHERE client_ip = '10.0.1.166' ORDER BY 34 |10.0.1.166 |2001:cafe::ff07:bdcc:bc59:ff9e ; +filterExactMatchIpv4WithIn_CastAsIP +SELECT id, client_ip, dest_ip FROM logs WHERE dest_ip IN (CAST('172.16.1.1' AS IP), CAST('2001:cafe::13e1:16fc:8726:1bf8' AS IP)) ORDER BY id DESC LIMIT 3; + + id | client_ip | dest_ip +---------------+---------------+------------------------------ +100 |10.0.0.129 |172.16.1.1 +78 |10.0.1.199 |2001:cafe::13e1:16fc:8726:1bf8 +69 |10.0.1.166 |2001:cafe::13e1:16fc:8726:1bf8 +; + +filterExactMatchIpv4WithIn_CastAsString +SELECT id, client_ip, dest_ip FROM logs WHERE CAST(dest_ip AS STRING) IN ('172.16.1.1', '2001:cafe::13e1:16fc:8726:1bf8') ORDER BY id DESC LIMIT 3; + + id | client_ip | dest_ip +---------------+---------------+------------------------------ +100 |10.0.0.129 |172.16.1.1 +78 |10.0.1.199 |2001:cafe::13e1:16fc:8726:1bf8 +69 |10.0.1.166 |2001:cafe::13e1:16fc:8726:1bf8 +; + filterExactMatchIpv6 SELECT id, client_ip, dest_ip FROM logs WHERE dest_ip = 'fe80::86ba:3bff:fe05:c3f3' ORDER BY id LIMIT 10;