SQL: Implement CAST between STRING and IP (#34949)

This allows comparison between IP fields and STRING input
from the user where automatic conversion is not yet supported (e.g.: for IN)

Closes: #34799
This commit is contained in:
Marios Trivyzas 2018-10-29 13:06:30 +01:00 committed by GitHub
parent 731e48beff
commit 8d7889d20c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 2 deletions

View File

@ -388,6 +388,8 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
case "varchar":
case "string":
return DataType.KEYWORD;
case "ip":
return DataType.IP;
default:
throw new ParsingException(source(ctx), "Does not recognize type {}", type);
}

View File

@ -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 @@ public abstract class DataTypeConversion {
case KEYWORD:
case TEXT:
return conversionToString(from);
case IP:
return conversionToIp(from);
case LONG:
return conversionToLong(from);
case INTEGER:
@ -146,6 +149,13 @@ public abstract class DataTypeConversion {
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 abstract class DataTypeConversion {
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<Object, Object> converter;
@ -464,4 +481,4 @@ public abstract class DataTypeConversion {
return dataType.isInteger ? dataType : LONG;
}
}
}

View File

@ -7,10 +7,13 @@ package org.elasticsearch.xpack.sql.type;
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 class DataTypeConversionTests extends ESTestCase {
() -> 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"))));
}
}

View File

@ -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;