diff --git a/docs/changelog/65145.yaml b/docs/changelog/65145.yaml new file mode 100644 index 0000000000000..b2b09cca54a6b --- /dev/null +++ b/docs/changelog/65145.yaml @@ -0,0 +1,6 @@ +pr: 65145 +summary: Add `unsigned_long` type support +area: Query Languages +type: enhancement +issues: + - 63312 diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/ExpressionBuilder.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/ExpressionBuilder.java index ed1bfa8d3378c..381d7e2ecfd38 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/ExpressionBuilder.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/parser/ExpressionBuilder.java @@ -49,7 +49,6 @@ import org.elasticsearch.xpack.ql.expression.predicate.operator.comparison.LessThanOrEqual; import org.elasticsearch.xpack.ql.expression.predicate.regex.Like; import org.elasticsearch.xpack.ql.tree.Source; -import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.ql.util.StringUtils; @@ -240,10 +239,9 @@ public Literal visitIntegerLiteral(EqlBaseParser.IntegerLiteralContext ctx) { Source source = source(ctx); String text = ctx.getText(); - long value; - try { - value = Long.valueOf(StringUtils.parseLong(text)); + Number value = StringUtils.parseIntegral(text); + return new Literal(source, value, DataTypes.fromJava(value)); } catch (QlIllegalArgumentException siae) { // if it's too large, then quietly try to parse as a float instead try { @@ -252,16 +250,6 @@ public Literal visitIntegerLiteral(EqlBaseParser.IntegerLiteralContext ctx) { throw new ParsingException(source, siae.getMessage()); } - - Object val = Long.valueOf(value); - DataType type = DataTypes.LONG; - - // try to downsize to int if possible (since that's the most common type) - if ((int) value == value) { - type = DataTypes.INTEGER; - val = Integer.valueOf((int) value); - } - return new Literal(source, val, type); } @Override diff --git a/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt b/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt index c3722d377f39d..cbdd21d451ea5 100644 --- a/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt +++ b/x-pack/plugin/eql/src/main/resources/org/elasticsearch/xpack/eql/plugin/eql_whitelist.txt @@ -19,6 +19,7 @@ class org.elasticsearch.xpack.ql.expression.function.scalar.whitelist.InternalQl double nullSafeSortNumeric(Number) String nullSafeSortString(Object) Number nullSafeCastNumeric(Number, String) + Number nullSafeCastToUnsignedLong(Number) # # ASCII Functions diff --git a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/analysis/VerifierTests.java b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/analysis/VerifierTests.java index 220bb0d60cbf4..5abd4dfee9f82 100644 --- a/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/analysis/VerifierTests.java +++ b/x-pack/plugin/eql/src/test/java/org/elasticsearch/xpack/eql/analysis/VerifierTests.java @@ -237,6 +237,7 @@ public void testNumeric() { accept(idxr, "foo where float_field == 0"); accept(idxr, "foo where half_float_field == 0"); accept(idxr, "foo where scaled_float_field == 0"); + accept(idxr, "foo where unsigned_long_field == 0"); // Test query against unsupported field type int assertEquals( diff --git a/x-pack/plugin/eql/src/test/resources/mapping-numeric.json b/x-pack/plugin/eql/src/test/resources/mapping-numeric.json index 1bf72719a9ff3..4f0587f35c4e0 100644 --- a/x-pack/plugin/eql/src/test/resources/mapping-numeric.json +++ b/x-pack/plugin/eql/src/test/resources/mapping-numeric.json @@ -34,6 +34,9 @@ "scaled_float_field": { "type" : "scaled_float" }, + "unsigned_long_field" : { + "type": "unsigned_long" + }, "wrong_int_type_field": { "type" : "int" } diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/execution/search/QlSourceBuilder.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/execution/search/QlSourceBuilder.java index 946fa8797ccba..fdcd87d4ef7a7 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/execution/search/QlSourceBuilder.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/execution/search/QlSourceBuilder.java @@ -22,7 +22,6 @@ * the resulting ES document as a field. */ public class QlSourceBuilder { - public static final Version SWITCH_TO_FIELDS_API_VERSION = Version.V_7_10_0; public static final Version INTRODUCING_MISSING_ORDER_IN_COMPOSITE_AGGS_VERSION = Version.V_7_16_0; // The LinkedHashMaps preserve the order of the fields in the response private final Set fetchFields = new LinkedHashSet<>(); diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/scalar/ScalarFunction.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/scalar/ScalarFunction.java index 58c15c769ddb5..5de41b3a43668 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/scalar/ScalarFunction.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/scalar/ScalarFunction.java @@ -12,6 +12,7 @@ import org.elasticsearch.xpack.ql.expression.function.Function; import org.elasticsearch.xpack.ql.expression.function.aggregate.AggregateFunction; import org.elasticsearch.xpack.ql.expression.function.grouping.GroupingFunction; +import org.elasticsearch.xpack.ql.expression.gen.script.Params; import org.elasticsearch.xpack.ql.expression.gen.script.ParamsBuilder; import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate; import org.elasticsearch.xpack.ql.expression.gen.script.Scripts; @@ -24,10 +25,12 @@ import java.util.List; import static java.util.Collections.emptyList; +import static org.elasticsearch.common.logging.LoggerMessageFormat.format; import static org.elasticsearch.xpack.ql.expression.gen.script.ParamsBuilder.paramsBuilder; import static org.elasticsearch.xpack.ql.expression.gen.script.Scripts.PARAM; import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; import static org.elasticsearch.xpack.ql.type.DataTypes.LONG; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; /** * A {@code ScalarFunction} is a {@code Function} that takes values from some @@ -150,7 +153,15 @@ protected ScriptTemplate scriptWithGrouping(GroupingFunction grouping) { } protected ScriptTemplate scriptWithField(FieldAttribute field) { - return new ScriptTemplate(processScript(Scripts.DOC_VALUE), paramsBuilder().variable(field.name()).build(), dataType()); + Params params = paramsBuilder().variable(field.name()).build(); + // unsigned_long fields get returned in scripts as plain longs, so a conversion is required + return field.dataType() != UNSIGNED_LONG + ? new ScriptTemplate(processScript(Scripts.DOC_VALUE), params, dataType()) + : new ScriptTemplate( + processScript(format("{ql}.", "nullSafeCastToUnsignedLong({})", Scripts.DOC_VALUE)), + params, + UNSIGNED_LONG + ); } protected String processScript(String script) { diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/scalar/whitelist/InternalQlScriptUtils.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/scalar/whitelist/InternalQlScriptUtils.java index c975c876c9b12..25a6575dc7411 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/scalar/whitelist/InternalQlScriptUtils.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/function/scalar/whitelist/InternalQlScriptUtils.java @@ -23,6 +23,7 @@ import java.util.Map; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.convert; +import static org.elasticsearch.xpack.ql.type.DataTypeConverter.toUnsignedLong; import static org.elasticsearch.xpack.ql.type.DataTypes.fromTypeName; public class InternalQlScriptUtils { @@ -58,6 +59,10 @@ public static Number nullSafeCastNumeric(Number number, String typeName) { return number == null || Double.isNaN(number.doubleValue()) ? null : (Number) convert(number, fromTypeName(typeName)); } + public static Number nullSafeCastToUnsignedLong(Number number) { + return number == null || Double.isNaN(number.doubleValue()) ? null : toUnsignedLong(number); + } + // // Operators // diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/arithmetic/Arithmetics.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/arithmetic/Arithmetics.java index 3948155b1c525..2543b62652795 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/arithmetic/Arithmetics.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/arithmetic/Arithmetics.java @@ -8,8 +8,11 @@ import org.elasticsearch.xpack.ql.QlIllegalArgumentException; +import java.math.BigInteger; import java.util.function.BiFunction; +import static org.elasticsearch.xpack.ql.util.NumericUtils.asUnsignedLong; + /** * Arithmetic operation using the type widening rules of the JLS 5.6.2 namely * widen to double or float or long or int in this order. @@ -43,6 +46,10 @@ public static Number add(Number l, Number r) { if (l instanceof Float || r instanceof Float) { return Float.valueOf(l.floatValue() + r.floatValue()); } + if (l instanceof BigInteger || r instanceof BigInteger) { + BigInteger bi = asBigInteger(l).add(asBigInteger(r)); + return asUnsignedLong(bi); + } if (l instanceof Long || r instanceof Long) { return Long.valueOf(Math.addExact(l.longValue(), r.longValue())); } @@ -61,6 +68,10 @@ public static Number sub(Number l, Number r) { if (l instanceof Float || r instanceof Float) { return Float.valueOf(l.floatValue() - r.floatValue()); } + if (l instanceof BigInteger || r instanceof BigInteger) { + BigInteger bi = asBigInteger(l).subtract(asBigInteger(r)); + return asUnsignedLong(bi); + } if (l instanceof Long || r instanceof Long) { return Long.valueOf(Math.subtractExact(l.longValue(), r.longValue())); } @@ -79,6 +90,13 @@ public static Number mul(Number l, Number r) { if (l instanceof Float || r instanceof Float) { return Float.valueOf(l.floatValue() * r.floatValue()); } + if (l instanceof BigInteger || r instanceof BigInteger) { + BigInteger bi = asBigInteger(l).multiply(asBigInteger(r)); + // Note: in case of unsigned_long overflow (or underflow, with negative fixed point numbers), the exception is thrown. + // This is unlike the way some other traditional RDBMS that support unsigned types work, which simply promote the result to a + // floating point type, but in line with how our implementation treats other fixed point type operations (i.e. Math#xxExact()). + return asUnsignedLong(bi); + } if (l instanceof Long || r instanceof Long) { return Long.valueOf(Math.multiplyExact(l.longValue(), r.longValue())); } @@ -97,6 +115,10 @@ public static Number div(Number l, Number r) { if (l instanceof Float || r instanceof Float) { return l.floatValue() / r.floatValue(); } + if (l instanceof BigInteger || r instanceof BigInteger) { + BigInteger bi = asBigInteger(l).divide(asBigInteger(r)); + return asUnsignedLong(bi); + } if (l instanceof Long || r instanceof Long) { return l.longValue() / r.longValue(); } @@ -115,6 +137,10 @@ public static Number mod(Number l, Number r) { if (l instanceof Float || r instanceof Float) { return Float.valueOf(l.floatValue() % r.floatValue()); } + if (l instanceof BigInteger || r instanceof BigInteger) { + BigInteger bi = asBigInteger(l).remainder(asBigInteger(r)); + return asUnsignedLong(bi); + } if (l instanceof Long || r instanceof Long) { return Long.valueOf(l.longValue() % r.longValue()); } @@ -141,10 +167,20 @@ static Number negate(Number n) { } return Float.valueOf(-n.floatValue()); } + if (n instanceof BigInteger) { + if (((BigInteger) n).signum() != 0) { + throw new ArithmeticException("unsigned_long overflow"); // in the scope of the unsigned_long type + } + return n; + } if (n instanceof Long) { return Long.valueOf(Math.negateExact(n.longValue())); } return Integer.valueOf(Math.negateExact(n.intValue())); } + + public static BigInteger asBigInteger(Number n) { + return n instanceof BigInteger ? (BigInteger) n : BigInteger.valueOf(n.longValue()); + } } diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/Comparisons.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/Comparisons.java index cb4d94615cddb..cbe032c253ad8 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/Comparisons.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/Comparisons.java @@ -6,8 +6,11 @@ */ package org.elasticsearch.xpack.ql.expression.predicate.operator.comparison; +import java.math.BigInteger; import java.util.Set; +import static org.elasticsearch.xpack.ql.expression.predicate.operator.arithmetic.Arithmetics.asBigInteger; + /** * Comparison utilities. */ @@ -92,6 +95,9 @@ private static Integer compare(Number l, Number r) { if (l instanceof Float || r instanceof Float) { return Float.compare(l.floatValue(), r.floatValue()); } + if (l instanceof BigInteger || r instanceof BigInteger) { + return asBigInteger(l).compareTo(asBigInteger(r)); + } if (l instanceof Long || r instanceof Long) { return Long.compare(l.longValue(), r.longValue()); } diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/IndexCompatibility.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/IndexCompatibility.java new file mode 100644 index 0000000000000..2f99854679d8f --- /dev/null +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/IndexCompatibility.java @@ -0,0 +1,49 @@ +/* + * 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.ql.index; + +import org.elasticsearch.Version; +import org.elasticsearch.xpack.ql.type.DataType; +import org.elasticsearch.xpack.ql.type.EsField; +import org.elasticsearch.xpack.ql.type.UnsupportedEsField; + +import java.util.Map; + +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.isTypeSupportedInVersion; +import static org.elasticsearch.xpack.ql.type.DataTypes.isPrimitive; +import static org.elasticsearch.xpack.ql.type.Types.propagateUnsupportedType; + +public final class IndexCompatibility { + + public static Map compatible(Map mapping, Version version) { + for (Map.Entry entry : mapping.entrySet()) { + EsField esField = entry.getValue(); + DataType dataType = esField.getDataType(); + if (isPrimitive(dataType) == false) { + compatible(esField.getProperties(), version); + } else if (isTypeSupportedInVersion(dataType, version) == false) { + EsField field = new UnsupportedEsField(entry.getKey(), dataType.name(), null, esField.getProperties()); + entry.setValue(field); + propagateUnsupportedType(entry.getKey(), dataType.name(), esField.getProperties()); + } + } + return mapping; + } + + public static EsIndex compatible(EsIndex esIndex, Version version) { + compatible(esIndex.mapping(), version); + return esIndex; + } + + public static IndexResolution compatible(IndexResolution indexResolution, Version version) { + if (indexResolution.isValid()) { + compatible(indexResolution.get(), version); + } + return indexResolution; + } +} diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/VersionCompatibilityChecks.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/VersionCompatibilityChecks.java new file mode 100644 index 0000000000000..f239e6bc8b08c --- /dev/null +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/index/VersionCompatibilityChecks.java @@ -0,0 +1,44 @@ +/* + * 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.ql.index; + +import org.elasticsearch.Version; +import org.elasticsearch.core.Nullable; +import org.elasticsearch.xpack.ql.type.DataType; + +import static org.elasticsearch.Version.V_8_2_0; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; + +public final class VersionCompatibilityChecks { + + public static final Version INTRODUCING_UNSIGNED_LONG = V_8_2_0; + + private VersionCompatibilityChecks() {} + + public static boolean isTypeSupportedInVersion(DataType dataType, Version version) { + if (dataType == UNSIGNED_LONG) { + return supportsUnsignedLong(version); + } + return true; + } + + /** + * Does the provided {@code version} support the unsigned_long type (PR#60050)? + */ + public static boolean supportsUnsignedLong(Version version) { + return INTRODUCING_UNSIGNED_LONG.compareTo(version) <= 0; + } + + public static @Nullable Version versionIntroducingType(DataType dataType) { + if (dataType == UNSIGNED_LONG) { + return INTRODUCING_UNSIGNED_LONG; + } + + return null; + } +} diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataTypeConverter.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataTypeConverter.java index d28202515f8e8..c2319ab8bde9d 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataTypeConverter.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataTypeConverter.java @@ -13,12 +13,13 @@ import org.elasticsearch.xpack.ql.QlIllegalArgumentException; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import java.util.Locale; import java.util.function.DoubleFunction; import java.util.function.Function; -import java.util.function.LongFunction; import static org.elasticsearch.xpack.ql.type.DataTypes.BOOLEAN; import static org.elasticsearch.xpack.ql.type.DataTypes.BYTE; @@ -32,9 +33,13 @@ import static org.elasticsearch.xpack.ql.type.DataTypes.NULL; import static org.elasticsearch.xpack.ql.type.DataTypes.SHORT; import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.ql.type.DataTypes.isDateTime; import static org.elasticsearch.xpack.ql.type.DataTypes.isPrimitive; import static org.elasticsearch.xpack.ql.type.DataTypes.isString; +import static org.elasticsearch.xpack.ql.util.NumericUtils.UNSIGNED_LONG_MAX; +import static org.elasticsearch.xpack.ql.util.NumericUtils.inUnsignedLongRange; +import static org.elasticsearch.xpack.ql.util.NumericUtils.isUnsignedLong; /** * Conversion utility from one Elasticsearch data type to another Elasticsearch data types. @@ -74,6 +79,9 @@ public static DataType commonType(DataType left, DataType right) { if (left.isInteger()) { // promote the highest int if (right.isInteger()) { + if (left == UNSIGNED_LONG || right == UNSIGNED_LONG) { + return UNSIGNED_LONG; + } return left.size() > right.size() ? left : right; } // promote the rational @@ -135,6 +143,9 @@ public static Converter converterFor(DataType from, DataType to) { if (to == LONG) { return conversionToLong(from); } + if (to == UNSIGNED_LONG) { + return conversionToUnsignedLong(from); + } if (to == INTEGER) { return conversionToInt(from); } @@ -176,6 +187,25 @@ private static Converter conversionToIp(DataType from) { return null; } + private static Converter conversionToUnsignedLong(DataType from) { + if (from.isRational()) { + return DefaultConverter.RATIONAL_TO_UNSIGNED_LONG; + } + if (from.isInteger()) { + return DefaultConverter.INTEGER_TO_UNSIGNED_LONG; + } + if (from == BOOLEAN) { + return DefaultConverter.BOOL_TO_UNSIGNED_LONG; + } + if (isString(from)) { + return DefaultConverter.STRING_TO_UNSIGNED_LONG; + } + if (from == DATETIME) { + return DefaultConverter.DATETIME_TO_UNSIGNED_LONG; + } + return null; + } + private static Converter conversionToLong(DataType from) { if (from.isRational()) { return DefaultConverter.RATIONAL_TO_LONG; @@ -340,15 +370,59 @@ public static int safeToInt(long x) { return (int) x; } - public static long safeToLong(double x) { + public static long safeDoubleToLong(double x) { if (x > Long.MAX_VALUE || x < Long.MIN_VALUE) { throw new QlIllegalArgumentException("[" + x + "] out of [long] range"); } return Math.round(x); } + public static Long safeToLong(Number x) { + try { + if (x instanceof BigInteger) { + return ((BigInteger) x).longValueExact(); + } + // integer converters are also provided double values (aggs generated on integer fields) + if (x instanceof Double || x instanceof Float) { + return safeDoubleToLong(x.doubleValue()); + } + return x.longValue(); + } catch (ArithmeticException ae) { + throw new QlIllegalArgumentException("[" + x + "] out of [long] range", ae); + } + } + + public static BigInteger safeToUnsignedLong(Double x) { + if (inUnsignedLongRange(x) == false) { + throw new QlIllegalArgumentException("[" + x + "] out of [unsigned_long] range"); + } + return BigDecimal.valueOf(x).toBigInteger(); + } + + public static BigInteger safeToUnsignedLong(Long x) { + if (x < 0) { + throw new QlIllegalArgumentException("[" + x + "] out of [unsigned_long] range"); + } + return BigInteger.valueOf(x); + } + + public static BigInteger safeToUnsignedLong(String x) { + BigInteger bi = new BigDecimal(x).toBigInteger(); + if (isUnsignedLong(bi) == false) { + throw new QlIllegalArgumentException("[" + x + "] out of [unsigned_long] range"); + } + return bi; + } + + // "unsafe" value conversion to unsigned long (vs. "safe", type-only conversion of safeToUnsignedLong()); + // -1L -> 18446744073709551615 (=UNSIGNED_LONG_MAX) + public static BigInteger toUnsignedLong(Number number) { + BigInteger bi = BigInteger.valueOf(number.longValue()); + return bi.signum() < 0 ? bi.and(UNSIGNED_LONG_MAX) : bi; + } + public static Number toInteger(double x, DataType dataType) { - long l = safeToLong(x); + long l = safeDoubleToLong(x); if (dataType == BYTE) { return safeToByte(l); @@ -405,38 +479,43 @@ public enum DefaultConverter implements Converter { DATETIME_TO_STRING(o -> DateUtils.toString((ZonedDateTime) o)), OTHER_TO_STRING(String::valueOf), - RATIONAL_TO_LONG(fromDouble(DataTypeConverter::safeToLong)), - INTEGER_TO_LONG(fromLong(value -> value)), + RATIONAL_TO_UNSIGNED_LONG(fromDouble(DataTypeConverter::safeToUnsignedLong)), + INTEGER_TO_UNSIGNED_LONG(fromNumber(value -> DataTypeConverter.safeToUnsignedLong(value.longValue()))), + STRING_TO_UNSIGNED_LONG(fromString(DataTypeConverter::safeToUnsignedLong, "unsigned_long")), + DATETIME_TO_UNSIGNED_LONG(fromDateTime(DataTypeConverter::safeToUnsignedLong)), + + RATIONAL_TO_LONG(fromDouble(DataTypeConverter::safeDoubleToLong)), + INTEGER_TO_LONG(fromNumber(DataTypeConverter::safeToLong)), STRING_TO_LONG(fromString(Long::valueOf, "long")), DATETIME_TO_LONG(fromDateTime(value -> value)), - RATIONAL_TO_INT(fromDouble(value -> safeToInt(safeToLong(value)))), - INTEGER_TO_INT(fromLong(DataTypeConverter::safeToInt)), + RATIONAL_TO_INT(fromDouble(value -> safeToInt(safeDoubleToLong(value)))), + INTEGER_TO_INT(fromNumber(value -> safeToInt(safeToLong(value)))), BOOL_TO_INT(fromBool(value -> value ? 1 : 0)), STRING_TO_INT(fromString(Integer::valueOf, "integer")), DATETIME_TO_INT(fromDateTime(DataTypeConverter::safeToInt)), - RATIONAL_TO_SHORT(fromDouble(value -> safeToShort(safeToLong(value)))), - INTEGER_TO_SHORT(fromLong(DataTypeConverter::safeToShort)), + RATIONAL_TO_SHORT(fromDouble(value -> safeToShort(safeDoubleToLong(value)))), + INTEGER_TO_SHORT(fromNumber(value -> safeToShort(safeToLong(value)))), BOOL_TO_SHORT(fromBool(value -> value ? (short) 1 : (short) 0)), STRING_TO_SHORT(fromString(Short::valueOf, "short")), DATETIME_TO_SHORT(fromDateTime(DataTypeConverter::safeToShort)), - RATIONAL_TO_BYTE(fromDouble(value -> safeToByte(safeToLong(value)))), - INTEGER_TO_BYTE(fromLong(DataTypeConverter::safeToByte)), + RATIONAL_TO_BYTE(fromDouble(value -> safeToByte(safeDoubleToLong(value)))), + INTEGER_TO_BYTE(fromNumber(value -> safeToByte(safeToLong(value)))), BOOL_TO_BYTE(fromBool(value -> value ? (byte) 1 : (byte) 0)), STRING_TO_BYTE(fromString(Byte::valueOf, "byte")), DATETIME_TO_BYTE(fromDateTime(DataTypeConverter::safeToByte)), - // TODO floating point conversions are lossy but conversions to integer conversions are not. Are we ok with that? + // TODO floating point conversions are lossy but conversions to integer are not. Are we ok with that? RATIONAL_TO_FLOAT(fromDouble(value -> (float) value)), - INTEGER_TO_FLOAT(fromLong(value -> (float) value)), + INTEGER_TO_FLOAT(fromNumber(Number::floatValue)), BOOL_TO_FLOAT(fromBool(value -> value ? 1f : 0f)), STRING_TO_FLOAT(fromString(Float::valueOf, "float")), DATETIME_TO_FLOAT(fromDateTime(value -> (float) value)), RATIONAL_TO_DOUBLE(fromDouble(Double::valueOf)), - INTEGER_TO_DOUBLE(fromLong(Double::valueOf)), + INTEGER_TO_DOUBLE(fromNumber(Number::doubleValue)), BOOL_TO_DOUBLE(fromBool(value -> value ? 1d : 0d)), STRING_TO_DOUBLE(fromString(Double::valueOf, "double")), DATETIME_TO_DOUBLE(fromDateTime(Double::valueOf)), @@ -446,10 +525,11 @@ public enum DefaultConverter implements Converter { BOOL_TO_DATETIME(toDateTime(BOOL_TO_INT)), STRING_TO_DATETIME(fromString(DateUtils::asDateTime, "datetime")), - NUMERIC_TO_BOOLEAN(fromLong(value -> value != 0)), + NUMERIC_TO_BOOLEAN(fromDouble(value -> value != 0)), STRING_TO_BOOLEAN(fromString(DataTypeConverter::convertToBoolean, "boolean")), DATETIME_TO_BOOLEAN(fromDateTime(value -> value != 0)), + BOOL_TO_UNSIGNED_LONG(fromBool(value -> value ? BigInteger.ONE : BigInteger.ZERO)), BOOL_TO_LONG(fromBool(value -> value ? 1L : 0L)), STRING_TO_IP(o -> { @@ -471,11 +551,11 @@ private static Function fromDouble(DoubleFunction conver return (Object l) -> converter.apply(((Number) l).doubleValue()); } - private static Function fromLong(LongFunction converter) { - return (Object l) -> converter.apply(((Number) l).longValue()); + private static Function fromNumber(Function converter) { + return l -> converter.apply((Number) l); } - private static Function fromString(Function converter, String to) { + public static Function fromString(Function converter, String to) { return (Object value) -> { try { return converter.apply(value.toString()); diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataTypes.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataTypes.java index 5d103590b7666..69451e51a655e 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataTypes.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataTypes.java @@ -6,6 +6,7 @@ */ package org.elasticsearch.xpack.ql.type; +import java.math.BigInteger; import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Collection; @@ -31,6 +32,7 @@ public final class DataTypes { public static final DataType SHORT = new DataType("short", Short.BYTES, true, false, true); public static final DataType INTEGER = new DataType("integer", Integer.BYTES, true, false, true); public static final DataType LONG = new DataType("long", Long.BYTES, true, false, true); + public static final DataType UNSIGNED_LONG = new DataType("unsigned_long", Long.BYTES, true, false, true); // decimal numeric public static final DataType DOUBLE = new DataType("double", Double.BYTES, false, true, true); public static final DataType FLOAT = new DataType("float", Float.BYTES, false, true, true); @@ -58,6 +60,7 @@ public final class DataTypes { SHORT, INTEGER, LONG, + UNSIGNED_LONG, DOUBLE, FLOAT, HALF_FLOAT, @@ -106,6 +109,9 @@ public static DataType fromJava(Object value) { if (value instanceof Long) { return LONG; } + if (value instanceof BigInteger) { + return UNSIGNED_LONG; + } if (value instanceof Boolean) { return BOOLEAN; } @@ -152,7 +158,7 @@ public static boolean isNullOrNumeric(DataType t) { } public static boolean isSigned(DataType t) { - return t.isNumeric(); + return t.isNumeric() && t.equals(UNSIGNED_LONG) == false; } public static boolean isDateTime(DataType type) { diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/Types.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/Types.java index 515d70dff05c8..c202e797b7566 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/Types.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/Types.java @@ -121,7 +121,7 @@ private static int intSetting(Object value, int defaultValue) { return value == null ? defaultValue : Integer.parseInt(value.toString()); } - private static void propagateUnsupportedType(String inherited, String originalType, Map properties) { + public static void propagateUnsupportedType(String inherited, String originalType, Map properties) { if (properties != null && properties.isEmpty() == false) { for (Entry entry : properties.entrySet()) { EsField field = entry.getValue(); diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/NumericUtils.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/NumericUtils.java new file mode 100644 index 0000000000000..5ec05397816c9 --- /dev/null +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/NumericUtils.java @@ -0,0 +1,37 @@ +/* + * 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.ql.util; + +import java.math.BigInteger; + +public abstract class NumericUtils { + // 18446744073709551615 + public static final BigInteger UNSIGNED_LONG_MAX = BigInteger.ONE.shiftLeft(Long.SIZE).subtract(BigInteger.ONE); + + // 18446744073709551615.0 + public static final double UNSIGNED_LONG_MAX_AS_DOUBLE = UNSIGNED_LONG_MAX.doubleValue(); + + public static boolean isUnsignedLong(BigInteger bi) { + return bi.signum() >= 0 && bi.compareTo(UNSIGNED_LONG_MAX) <= 0; + } + + public static boolean inUnsignedLongRange(double d) { + // UNSIGNED_LONG_MAX can't be represented precisely enough on a double, being converted as a rounded up value. + // Converting it to a double and back will yield a larger unsigned long, so the double comparison is still preferred, but + // it'll require the equality check. (BigDecimal comparisons only make sense for string-recovered floating point numbers.) + // This also means that 18446744073709551615.0 is actually a double too high to be converted as an unsigned long. + return d >= 0 && d < UNSIGNED_LONG_MAX_AS_DOUBLE; + } + + public static BigInteger asUnsignedLong(BigInteger bi) { + if (isUnsignedLong(bi) == false) { + throw new ArithmeticException("unsigned_long overflow"); + } + return bi; + } +} diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java index d545835154b07..5f067aca76827 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/util/StringUtils.java @@ -26,6 +26,7 @@ import static java.util.stream.Collectors.toList; import static org.elasticsearch.transport.RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR; import static org.elasticsearch.transport.RemoteClusterAware.buildRemoteIndexName; +import static org.elasticsearch.xpack.ql.util.NumericUtils.isUnsignedLong; public final class StringUtils { @@ -300,6 +301,27 @@ public static long parseLong(String string) throws QlIllegalArgumentException { } } + public static Number parseIntegral(String string) throws QlIllegalArgumentException { + BigInteger bi; + try { + bi = new BigInteger(string); + } catch (NumberFormatException ex) { + throw new QlIllegalArgumentException("Cannot parse number [{}]", string); + } + if (bi.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { + if (isUnsignedLong(bi) == false) { + throw new QlIllegalArgumentException("Number [{}] is too large", string); + } + return bi; + } + // try to downsize to int if possible (since that's the most common type) + if (bi.intValue() == bi.longValue()) { // ternary operator would always promote to Long + return bi.intValueExact(); + } else { + return bi.longValueExact(); + } + } + public static String ordinal(int i) { return switch (i % 100) { case 11, 12, 13 -> i + "th"; diff --git a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessorTests.java b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessorTests.java index 2186926cbd50a..c47971e67bb65 100644 --- a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessorTests.java +++ b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessorTests.java @@ -15,6 +15,9 @@ import org.elasticsearch.xpack.ql.expression.gen.processor.ConstantProcessor; import org.elasticsearch.xpack.ql.expression.gen.processor.Processor; import org.elasticsearch.xpack.ql.expression.processor.Processors; +import org.elasticsearch.xpack.ql.util.NumericUtils; + +import java.math.BigInteger; import static org.elasticsearch.xpack.ql.tree.Source.EMPTY; @@ -47,16 +50,61 @@ public void testAdd() { assertEquals(10, ba.process(null)); } + public void testAddUnsignedLong() { + Processor ba = new Add(EMPTY, l(BigInteger.valueOf(7)), l(3)).makePipe().asProcessor(); + assertEquals(BigInteger.valueOf(10), ba.process(null)); + + ba = new Add(EMPTY, l(BigInteger.ONE), l(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE))).makePipe().asProcessor(); + assertEquals(BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.TWO), ba.process(null)); + + ba = new Add(EMPTY, l(BigInteger.valueOf(7)), l((short) -3)).makePipe().asProcessor(); + assertEquals(BigInteger.valueOf(4), ba.process(null)); + + ba = new Add(EMPTY, l(BigInteger.valueOf(7)), l(-3f)).makePipe().asProcessor(); + assertEquals(4f, ba.process(null)); + + Processor pn = new Add(EMPTY, l(BigInteger.valueOf(7)), l(-8)).makePipe().asProcessor(); + expectThrows(ArithmeticException.class, () -> pn.process(null)); + + Processor pm = new Add(EMPTY, l(NumericUtils.UNSIGNED_LONG_MAX), l(1)).makePipe().asProcessor(); + expectThrows(ArithmeticException.class, () -> pm.process(null)); + } + public void testSub() { Processor ba = new Sub(EMPTY, l(7), l(3)).makePipe().asProcessor(); assertEquals(4, ba.process(null)); } + public void testSubUnsignedLong() { + Processor bs = new Sub(EMPTY, l(BigInteger.valueOf(7)), l(3)).makePipe().asProcessor(); + assertEquals(BigInteger.valueOf(4), bs.process(null)); + + bs = new Sub(EMPTY, l(BigInteger.valueOf(7)), l((short) -3)).makePipe().asProcessor(); + assertEquals(BigInteger.valueOf(10), bs.process(null)); + + bs = new Sub(EMPTY, l(BigInteger.valueOf(7)), l(3f)).makePipe().asProcessor(); + assertEquals(4f, bs.process(null)); + + Processor proc = new Sub(EMPTY, l(BigInteger.valueOf(7)), l(8)).makePipe().asProcessor(); + expectThrows(ArithmeticException.class, () -> proc.process(null)); + } + public void testMul() { Processor ba = new Mul(EMPTY, l(7), l(3)).makePipe().asProcessor(); assertEquals(21, ba.process(null)); } + public void testMulUnsignedLong() { + Processor bm = new Mul(EMPTY, l(BigInteger.valueOf(7)), l(3)).makePipe().asProcessor(); + assertEquals(BigInteger.valueOf(21), bm.process(null)); + + bm = new Mul(EMPTY, l(BigInteger.valueOf(7)), l(3f)).makePipe().asProcessor(); + assertEquals(21f, bm.process(null)); + + Processor proc = new Mul(EMPTY, l(BigInteger.valueOf(7)), l(-8)).makePipe().asProcessor(); + expectThrows(ArithmeticException.class, () -> proc.process(null)); + } + public void testDiv() { Processor ba = new Div(EMPTY, l(7), l(3)).makePipe().asProcessor(); assertEquals(2, ((Number) ba.process(null)).longValue()); @@ -64,16 +112,46 @@ public void testDiv() { assertEquals(2.33, ((Number) ba.process(null)).doubleValue(), 0.01d); } + public void testDivUnsignedLong() { + Processor bd = new Div(EMPTY, l(BigInteger.valueOf(7)), l(3)).makePipe().asProcessor(); + assertEquals(BigInteger.TWO, bd.process(null)); + + bd = new Div(EMPTY, l(7), l(BigInteger.valueOf(8))).makePipe().asProcessor(); + assertEquals(BigInteger.ZERO, bd.process(null)); + + bd = new Div(EMPTY, l(BigInteger.valueOf(7)), l(3f)).makePipe().asProcessor(); + assertEquals(7 / 3f, bd.process(null)); + + Processor proc = new Div(EMPTY, l(BigInteger.valueOf(7)), l(-2)).makePipe().asProcessor(); + expectThrows(ArithmeticException.class, () -> proc.process(null)); + } + public void testMod() { Processor ba = new Mod(EMPTY, l(7), l(3)).makePipe().asProcessor(); assertEquals(1, ba.process(null)); } + public void testModUnsignedLong() { + Processor bm = new Mod(EMPTY, l(BigInteger.valueOf(7)), l(3)).makePipe().asProcessor(); + assertEquals(BigInteger.valueOf(1), bm.process(null)); + + Processor proc = new Mod(EMPTY, l(-7), l(BigInteger.valueOf(3))).makePipe().asProcessor(); + expectThrows(ArithmeticException.class, () -> proc.process(null)); + } + public void testNegate() { Processor ba = new Neg(EMPTY, l(7)).asPipe().asProcessor(); assertEquals(-7, ba.process(null)); } + public void testNegateUnsignedLong() { + Processor nm = new Neg(EMPTY, l(BigInteger.valueOf(0))).makePipe().asProcessor(); + assertEquals(BigInteger.ZERO, nm.process(null)); + + Processor proc = new Neg(EMPTY, l(BigInteger.valueOf(3))).makePipe().asProcessor(); + expectThrows(ArithmeticException.class, () -> proc.process(null)); + } + // ((3*2+4)/2-2)%2 public void testTree() { Expression mul = new Mul(EMPTY, l(3), l(2)); @@ -86,6 +164,18 @@ public void testTree() { assertEquals(1, proc.process(null)); } + // ((3*2+4)/2-2)%2 + public void testTreeUnsignedLong() { + Expression mul = new Mul(EMPTY, l(3), l(BigInteger.TWO)); + Expression add = new Add(EMPTY, mul, l(4)); + Expression div = new Div(EMPTY, add, l(2)); + Expression sub = new Sub(EMPTY, div, l(2)); + Mod mod = new Mod(EMPTY, sub, l(2)); + + Processor proc = mod.makePipe().asProcessor(); + assertEquals(BigInteger.ONE, proc.process(null)); + } + public void testHandleNull() { assertNull(new Add(EMPTY, l(null), l(3)).makePipe().asProcessor().process(null)); assertNull(new Sub(EMPTY, l(null), l(3)).makePipe().asProcessor().process(null)); diff --git a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/BinaryComparisonProcessorTests.java b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/BinaryComparisonProcessorTests.java index 6221f8414a74c..ddfed79feb3eb 100644 --- a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/BinaryComparisonProcessorTests.java +++ b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/expression/predicate/operator/comparison/BinaryComparisonProcessorTests.java @@ -14,6 +14,8 @@ import org.elasticsearch.xpack.ql.expression.gen.processor.ConstantProcessor; import org.elasticsearch.xpack.ql.expression.processor.Processors; +import java.math.BigInteger; + import static org.elasticsearch.xpack.ql.TestUtils.equalsOf; import static org.elasticsearch.xpack.ql.TestUtils.greaterThanOf; import static org.elasticsearch.xpack.ql.TestUtils.greaterThanOrEqualOf; @@ -51,6 +53,8 @@ protected NamedWriteableRegistry getNamedWriteableRegistry() { public void testEq() { assertEquals(true, equalsOf(l(4), l(4)).makePipe().asProcessor().process(null)); assertEquals(false, equalsOf(l(3), l(4)).makePipe().asProcessor().process(null)); + assertEquals(true, equalsOf(l(BigInteger.valueOf(4)), l(4L)).makePipe().asProcessor().process(null)); + assertEquals(false, equalsOf(l(BigInteger.valueOf(3)), l(4L)).makePipe().asProcessor().process(null)); } public void testNullEq() { @@ -64,30 +68,39 @@ public void testNullEq() { public void testNEq() { assertEquals(false, notEqualsOf(l(4), l(4)).makePipe().asProcessor().process(null)); assertEquals(true, notEqualsOf(l(3), l(4)).makePipe().asProcessor().process(null)); + assertEquals(true, notEqualsOf(l(BigInteger.valueOf(3)), l(4)).makePipe().asProcessor().process(null)); } public void testGt() { assertEquals(true, greaterThanOf(l(4), l(3)).makePipe().asProcessor().process(null)); assertEquals(false, greaterThanOf(l(3), l(4)).makePipe().asProcessor().process(null)); assertEquals(false, greaterThanOf(l(3), l(3)).makePipe().asProcessor().process(null)); + assertEquals(true, greaterThanOf(l(4), l(BigInteger.valueOf(3))).makePipe().asProcessor().process(null)); } public void testGte() { assertEquals(true, greaterThanOrEqualOf(l(4), l(3)).makePipe().asProcessor().process(null)); assertEquals(false, greaterThanOrEqualOf(l(3), l(4)).makePipe().asProcessor().process(null)); assertEquals(true, greaterThanOrEqualOf(l(3), l(3)).makePipe().asProcessor().process(null)); + assertEquals(true, greaterThanOrEqualOf(l(BigInteger.valueOf(3)), l(3L)).makePipe().asProcessor().process(null)); + assertEquals(true, greaterThanOrEqualOf(l(BigInteger.valueOf(4)), l(3L)).makePipe().asProcessor().process(null)); + assertEquals(false, greaterThanOrEqualOf(l(BigInteger.valueOf(3)), l(4L)).makePipe().asProcessor().process(null)); } public void testLt() { assertEquals(false, lessThanOf(l(4), l(3)).makePipe().asProcessor().process(null)); assertEquals(true, lessThanOf(l(3), l(4)).makePipe().asProcessor().process(null)); assertEquals(false, lessThanOf(l(3), l(3)).makePipe().asProcessor().process(null)); + assertEquals(false, lessThanOf(l(3), l(BigInteger.valueOf(3))).makePipe().asProcessor().process(null)); } public void testLte() { assertEquals(false, lessThanOrEqualOf(l(4), l(3)).makePipe().asProcessor().process(null)); assertEquals(true, lessThanOrEqualOf(l(3), l(4)).makePipe().asProcessor().process(null)); assertEquals(true, lessThanOrEqualOf(l(3), l(3)).makePipe().asProcessor().process(null)); + assertEquals(false, lessThanOrEqualOf(l(4), l(BigInteger.valueOf(3))).makePipe().asProcessor().process(null)); + assertEquals(true, lessThanOrEqualOf(l(3), l(BigInteger.valueOf(4))).makePipe().asProcessor().process(null)); + assertEquals(true, lessThanOrEqualOf(l(3), l(BigInteger.valueOf(3))).makePipe().asProcessor().process(null)); } public void testHandleNull() { diff --git a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/type/DataTypeConversionTests.java b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/type/DataTypeConversionTests.java index 0df878003ffe0..13a1182c1f7f6 100644 --- a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/type/DataTypeConversionTests.java +++ b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/type/DataTypeConversionTests.java @@ -12,6 +12,8 @@ import org.elasticsearch.xpack.ql.tree.Location; import org.elasticsearch.xpack.ql.tree.Source; +import java.math.BigDecimal; +import java.math.BigInteger; import java.time.ZonedDateTime; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.commonType; @@ -28,8 +30,10 @@ import static org.elasticsearch.xpack.ql.type.DataTypes.NULL; import static org.elasticsearch.xpack.ql.type.DataTypes.SHORT; import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.ql.type.DataTypes.UNSUPPORTED; import static org.elasticsearch.xpack.ql.type.DateUtils.asDateTime; +import static org.elasticsearch.xpack.ql.util.NumericUtils.UNSIGNED_LONG_MAX; public class DataTypeConversionTests extends ESTestCase { @@ -40,6 +44,12 @@ public void testConversionToString() { assertNull(conversion.convert(null)); assertEquals("10.0", conversion.convert(10.0)); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + BigInteger bi = randomBigInteger(); + assertEquals(bi.toString(), conversion.convert(bi)); + } { Converter conversion = converterFor(DATETIME, to); assertNull(conversion.convert(null)); @@ -63,6 +73,16 @@ public void testConversionToLong() { Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(Double.MAX_VALUE)); assertEquals("[" + Double.MAX_VALUE + "] out of [long] range", e.getMessage()); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + BigInteger bi = BigInteger.valueOf(randomNonNegativeLong()); + assertEquals(bi.longValue(), conversion.convert(bi)); + + BigInteger longPlus = bi.add(BigInteger.valueOf(Long.MAX_VALUE)); + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(longPlus)); + assertEquals("[" + longPlus + "] out of [long] range", e.getMessage()); + } { Converter conversion = converterFor(INTEGER, to); assertNull(conversion.convert(null)); @@ -104,6 +124,16 @@ public void testConversionToDateTime() { Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(Double.MAX_VALUE)); assertEquals("[" + Double.MAX_VALUE + "] out of [long] range", e.getMessage()); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + BigInteger bi = BigInteger.valueOf(randomNonNegativeLong()); + assertEquals(asDateTime(bi.longValue()), conversion.convert(bi)); + + BigInteger longPlus = bi.add(BigInteger.valueOf(Long.MAX_VALUE)); + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(longPlus)); + assertEquals("[" + longPlus + "] out of [long] range", e.getMessage()); + } { Converter conversion = converterFor(INTEGER, to); assertNull(conversion.convert(null)); @@ -157,6 +187,13 @@ public void testConversionToFloat() { assertEquals(10.1f, (float) conversion.convert(10.1d), 0.00001); assertEquals(10.6f, (float) conversion.convert(10.6d), 0.00001); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + + BigInteger bi = randomBigInteger(); + assertEquals(bi.floatValue(), (float) conversion.convert(bi), 0); + } { Converter conversion = converterFor(INTEGER, to); assertNull(conversion.convert(null)); @@ -197,6 +234,13 @@ public void testConversionToDouble() { assertEquals(10.1, (double) conversion.convert(10.1f), 0.00001); assertEquals(10.6, (double) conversion.convert(10.6f), 0.00001); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + + BigInteger bi = randomBigInteger(); + assertEquals(bi.doubleValue(), (double) conversion.convert(bi), 0); + } { Converter conversion = converterFor(INTEGER, to); assertNull(conversion.convert(null)); @@ -237,6 +281,12 @@ public void testConversionToBoolean() { assertEquals(true, conversion.convert(-10.0f)); assertEquals(false, conversion.convert(0.0f)); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + assertEquals(true, conversion.convert(BigInteger.valueOf(randomNonNegativeLong()))); + assertEquals(false, conversion.convert(BigInteger.ZERO)); + } { Converter conversion = converterFor(INTEGER, to); assertNull(conversion.convert(null)); @@ -289,6 +339,67 @@ public void testConversionToBoolean() { } } + public void testConversionToUnsignedLong() { + DataType to = UNSIGNED_LONG; + { + Converter conversion = converterFor(DOUBLE, to); + assertNull(conversion.convert(null)); + double d = Math.abs(randomDouble()); + assertEquals(BigDecimal.valueOf(d).toBigInteger(), conversion.convert(d)); + + Double ulmAsDouble = UNSIGNED_LONG_MAX.doubleValue(); + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(ulmAsDouble)); + assertEquals("[" + ulmAsDouble + "] out of [unsigned_long] range", e.getMessage()); + + Double nd = -Math.abs(randomDouble()); + e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(nd)); + assertEquals("[" + nd + "] out of [unsigned_long] range", e.getMessage()); + } + { + Converter conversion = converterFor(LONG, to); + assertNull(conversion.convert(null)); + + BigInteger bi = BigInteger.valueOf(randomNonNegativeLong()); + assertEquals(bi, conversion.convert(bi.longValue())); + + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(bi.negate())); + assertEquals("[" + bi.negate() + "] out of [unsigned_long] range", e.getMessage()); + } + { + Converter conversion = converterFor(DATETIME, to); + assertNull(conversion.convert(null)); + + long l = randomNonNegativeLong(); + assertEquals(BigInteger.valueOf(l), conversion.convert(asDateTime(l))); + + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(asDateTime(-l))); + assertEquals("[" + -l + "] out of [unsigned_long] range", e.getMessage()); + } + { + Converter conversion = converterFor(BOOLEAN, to); + assertNull(conversion.convert(null)); + + assertEquals(BigInteger.ONE, conversion.convert(true)); + assertEquals(BigInteger.ZERO, conversion.convert(false)); + } + { + Converter conversion = converterFor(KEYWORD, to); + assertNull(conversion.convert(null)); + BigInteger bi = randomBigInteger(); + assertEquals(bi, conversion.convert(bi.toString())); + + assertEquals(UNSIGNED_LONG_MAX, conversion.convert(UNSIGNED_LONG_MAX.toString())); + assertEquals(UNSIGNED_LONG_MAX, conversion.convert(UNSIGNED_LONG_MAX.toString() + ".0")); + + assertEquals(bi, conversion.convert(bi.toString() + "." + randomNonNegativeLong())); + + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(BigInteger.ONE.negate().toString())); + assertEquals("[-1] out of [unsigned_long] range", e.getMessage()); + e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(UNSIGNED_LONG_MAX.add(BigInteger.ONE).toString())); + assertEquals("[" + UNSIGNED_LONG_MAX.add(BigInteger.ONE).toString() + "] out of [unsigned_long] range", e.getMessage()); + } + } + public void testConversionToInt() { DataType to = INTEGER; { @@ -300,6 +411,16 @@ public void testConversionToInt() { Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(Long.MAX_VALUE)); assertEquals("[" + Long.MAX_VALUE + "] out of [integer] range", e.getMessage()); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + BigInteger bi = BigInteger.valueOf(randomIntBetween(0, Integer.MAX_VALUE)); + assertEquals(bi.intValueExact(), conversion.convert(bi)); + + BigInteger bip = BigInteger.valueOf(randomLongBetween(Integer.MAX_VALUE, Long.MAX_VALUE)); + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(bip)); + assertEquals("[" + bip + "] out of [integer] range", e.getMessage()); + } { Converter conversion = converterFor(DATETIME, to); assertNull(conversion.convert(null)); @@ -324,6 +445,16 @@ public void testConversionToShort() { Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(Integer.MAX_VALUE)); assertEquals("[" + Integer.MAX_VALUE + "] out of [short] range", e.getMessage()); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + BigInteger bi = BigInteger.valueOf(randomIntBetween(0, Short.MAX_VALUE)); + assertEquals(bi.shortValueExact(), conversion.convert(bi)); + + BigInteger bip = BigInteger.valueOf(randomLongBetween(Short.MAX_VALUE, Long.MAX_VALUE)); + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(bip)); + assertEquals("[" + bip + "] out of [short] range", e.getMessage()); + } { Converter conversion = converterFor(DATETIME, to); assertNull(conversion.convert(null)); @@ -347,6 +478,16 @@ public void testConversionToByte() { Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(Short.MAX_VALUE)); assertEquals("[" + Short.MAX_VALUE + "] out of [byte] range", e.getMessage()); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + BigInteger bi = BigInteger.valueOf(randomIntBetween(0, Byte.MAX_VALUE)); + assertEquals(bi.byteValueExact(), conversion.convert(bi)); + + BigInteger bip = BigInteger.valueOf(randomLongBetween(Byte.MAX_VALUE, Long.MAX_VALUE)); + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(bip)); + assertEquals("[" + bip + "] out of [byte] range", e.getMessage()); + } { Converter conversion = converterFor(DATETIME, to); assertNull(conversion.convert(null)); @@ -387,7 +528,9 @@ public void testCommonType() { assertEquals(SHORT, commonType(SHORT, BYTE)); assertEquals(FLOAT, commonType(BYTE, FLOAT)); assertEquals(FLOAT, commonType(FLOAT, INTEGER)); + assertEquals(UNSIGNED_LONG, commonType(UNSIGNED_LONG, LONG)); assertEquals(DOUBLE, commonType(DOUBLE, FLOAT)); + assertEquals(FLOAT, commonType(FLOAT, UNSIGNED_LONG)); // strings assertEquals(TEXT, commonType(TEXT, KEYWORD)); diff --git a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/type/TypesTests.java b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/type/TypesTests.java index f565896416dce..fa916cebd73fc 100644 --- a/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/type/TypesTests.java +++ b/x-pack/plugin/ql/src/test/java/org/elasticsearch/xpack/ql/type/TypesTests.java @@ -228,7 +228,7 @@ public static Map loadMapping(DataTypeRegistry registry, String return loadMapping(registry, stream, ordered); } - public static Map loadMapping(DataTypeRegistry registry, InputStream stream, Boolean ordered) { + private static Map loadMapping(DataTypeRegistry registry, InputStream stream, Boolean ordered) { boolean order = ordered != null ? ordered.booleanValue() : randomBoolean(); try (InputStream in = stream) { return Types.fromEs(registry, XContentHelper.convertToMap(JsonXContent.jsonXContent, in, order)); diff --git a/x-pack/plugin/ql/src/test/resources/mapping-multi-field-variation.json b/x-pack/plugin/ql/src/test/resources/mapping-multi-field-variation.json index ecb0cd1017d6b..fa73523d7ea27 100644 --- a/x-pack/plugin/ql/src/test/resources/mapping-multi-field-variation.json +++ b/x-pack/plugin/ql/src/test/resources/mapping-multi-field-variation.json @@ -2,6 +2,7 @@ "properties" : { "bool" : { "type" : "boolean" }, "int" : { "type" : "integer" }, + "unsigned_long" : { "type" : "unsigned_long" }, "float" : { "type" : "float" }, "text" : { "type" : "text" }, "keyword" : { "type" : "keyword" }, diff --git a/x-pack/plugin/ql/src/test/resources/mapping-multi-field-with-nested.json b/x-pack/plugin/ql/src/test/resources/mapping-multi-field-with-nested.json index e85827aac857f..cf864fc56a0ec 100644 --- a/x-pack/plugin/ql/src/test/resources/mapping-multi-field-with-nested.json +++ b/x-pack/plugin/ql/src/test/resources/mapping-multi-field-with-nested.json @@ -2,6 +2,7 @@ "properties" : { "bool" : { "type" : "boolean" }, "int" : { "type" : "integer" }, + "unsigned_long" : { "type" : "unsigned_long" }, "text" : { "type" : "text" }, "keyword" : { "type" : "keyword" }, "unsupported" : { "type" : "ip_range" }, diff --git a/x-pack/plugin/ql/src/test/resources/mapping-numeric.json b/x-pack/plugin/ql/src/test/resources/mapping-numeric.json index 15b02ab5f311e..119be02a4f098 100644 --- a/x-pack/plugin/ql/src/test/resources/mapping-numeric.json +++ b/x-pack/plugin/ql/src/test/resources/mapping-numeric.json @@ -12,6 +12,9 @@ "long" : { "type" : "long" }, + "unsigned_long" : { + "type" : "unsigned_long" + }, "meta_subfield" : { "type" : "text", "fields" : { diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/EsType.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/EsType.java index 5b4a8afd2a40c..6fd6bb7e83d6e 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/EsType.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/EsType.java @@ -47,7 +47,8 @@ public enum EsType implements SQLType { INTERVAL_MINUTE_TO_SECOND(ExtraTypes.INTERVAL_MINUTE_SECOND), GEO_POINT(ExtraTypes.GEOMETRY), GEO_SHAPE(ExtraTypes.GEOMETRY), - SHAPE(ExtraTypes.GEOMETRY); + SHAPE(ExtraTypes.GEOMETRY), + UNSIGNED_LONG(Types.NUMERIC); private final Integer type; diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatement.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatement.java index 0b77f714f2540..17a15fc026703 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatement.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatement.java @@ -9,6 +9,7 @@ import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URL; import java.sql.Array; import java.sql.Blob; @@ -44,6 +45,7 @@ import java.util.Locale; import static java.time.ZoneOffset.UTC; +import static org.elasticsearch.xpack.sql.jdbc.TypeUtils.scaleOrLength; class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { @@ -209,7 +211,7 @@ public void setObject(int parameterIndex, Object x) throws SQLException { // {@code java.sql.Array} etc) will generate the correct exception message. Otherwise, the method call // {@code TypeConverter.fromJavaToJDBC(x.getClass())} will report the implementing class as not being supported. checkKnownUnsupportedTypes(x); - setObject(parameterIndex, x, TypeUtils.of(x.getClass()).getVendorTypeNumber(), 0); + setObject(parameterIndex, x, TypeUtils.of(x.getClass()).getVendorTypeNumber(), scaleOrLength(x)); } @Override @@ -353,7 +355,7 @@ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale @Override public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { - setObject(parameterIndex, x, TypeUtils.of(targetSqlType), targetSqlType.getName()); + setObject(parameterIndex, x, TypeUtils.of(targetSqlType, scaleOrLength), targetSqlType.getName()); } private void setObject(int parameterIndex, Object x, EsType dataType, String typeString) throws SQLException { @@ -418,6 +420,7 @@ private void setObject(int parameterIndex, Object x, EsType dataType, String typ || x instanceof Short || x instanceof Integer || x instanceof Long + || x instanceof BigInteger || x instanceof Float || x instanceof Double || x instanceof String) { diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcResultSetMetaData.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcResultSetMetaData.java index 4e8cffc8fc7e9..df23d3e30d520 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcResultSetMetaData.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcResultSetMetaData.java @@ -94,8 +94,7 @@ public int getPrecision(int column) throws SQLException { @Override public int getScale(int column) throws SQLException { - column(column); - return 0; + return column(column).displaySize(); } @Override diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeConverter.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeConverter.java index 1c0c22f3523da..a0717cb53ada3 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeConverter.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeConverter.java @@ -9,6 +9,7 @@ import org.elasticsearch.xpack.sql.proto.StringUtils; import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.Date; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; @@ -157,6 +158,9 @@ static T convert(Object val, EsType columnType, Class type, String typeSt if (type == Long.class) { return (T) asLong(val, columnType, typeString); } + if (type == BigInteger.class) { + return (T) asBigInteger(val, columnType, typeString); + } if (type == Float.class) { return (T) asFloat(val, columnType, typeString); } @@ -219,6 +223,8 @@ static Object convert(Object v, EsType columnType, String typeString) throws SQL return ((Number) v).intValue(); case LONG: return ((Number) v).longValue(); + case UNSIGNED_LONG: + return asBigInteger(v, columnType, typeString); case HALF_FLOAT: case SCALED_FLOAT: case DOUBLE: @@ -311,11 +317,12 @@ private static Boolean asBoolean(Object val, EsType columnType, String typeStrin case SHORT: case INTEGER: case LONG: + case UNSIGNED_LONG: case FLOAT: case HALF_FLOAT: case SCALED_FLOAT: case DOUBLE: - return Boolean.valueOf(Integer.signum(((Number) val).intValue()) != 0); + return Boolean.valueOf(((Number) val).doubleValue() != 0); case KEYWORD: case TEXT: return Boolean.valueOf((String) val); @@ -333,6 +340,8 @@ private static Byte asByte(Object val, EsType columnType, String typeString) thr case INTEGER: case LONG: return safeToByte(((Number) val).longValue()); + case UNSIGNED_LONG: + return safeToByte(asBigInteger(val, columnType, typeString)); case FLOAT: case HALF_FLOAT: case SCALED_FLOAT: @@ -360,6 +369,8 @@ private static Short asShort(Object val, EsType columnType, String typeString) t case INTEGER: case LONG: return safeToShort(((Number) val).longValue()); + case UNSIGNED_LONG: + return safeToShort(asBigInteger(val, columnType, typeString)); case FLOAT: case HALF_FLOAT: case SCALED_FLOAT: @@ -386,6 +397,8 @@ private static Integer asInteger(Object val, EsType columnType, String typeStrin case INTEGER: case LONG: return safeToInt(((Number) val).longValue()); + case UNSIGNED_LONG: + return safeToInt(asBigInteger(val, columnType, typeString)); case FLOAT: case HALF_FLOAT: case SCALED_FLOAT: @@ -412,6 +425,8 @@ private static Long asLong(Object val, EsType columnType, String typeString) thr case INTEGER: case LONG: return Long.valueOf(((Number) val).longValue()); + case UNSIGNED_LONG: + return safeToLong(asBigInteger(val, columnType, typeString)); case FLOAT: case HALF_FLOAT: case SCALED_FLOAT: @@ -444,6 +459,8 @@ private static Float asFloat(Object val, EsType columnType, String typeString) t case INTEGER: case LONG: return Float.valueOf(((Number) val).longValue()); + case UNSIGNED_LONG: + return asBigInteger(val, columnType, typeString).floatValue(); case FLOAT: case HALF_FLOAT: case SCALED_FLOAT: @@ -470,6 +487,8 @@ private static Double asDouble(Object val, EsType columnType, String typeString) case INTEGER: case LONG: return Double.valueOf(((Number) val).longValue()); + case UNSIGNED_LONG: + return asBigInteger(val, columnType, typeString).doubleValue(); case FLOAT: case HALF_FLOAT: case SCALED_FLOAT: @@ -524,6 +543,34 @@ private static byte[] asByteArray(Object val, EsType columnType, String typeStri throw new SQLFeatureNotSupportedException(); } + private static BigInteger asBigInteger(Object val, EsType columnType, String typeString) throws SQLException { + switch (columnType) { + case BOOLEAN: + return ((Boolean) val).booleanValue() ? BigInteger.ONE : BigInteger.ZERO; + case BYTE: + case SHORT: + case INTEGER: + case LONG: + return BigInteger.valueOf(((Number) val).longValue()); + case FLOAT: + case HALF_FLOAT: + case SCALED_FLOAT: + case DOUBLE: + return BigDecimal.valueOf(((Number) val).doubleValue()).toBigInteger(); + // Aggs can return floats dressed as UL types (bugUrl="https://github.com/elastic/elasticsearch/issues/65413") + case UNSIGNED_LONG: + case KEYWORD: + case TEXT: + try { + return new BigDecimal(val.toString()).toBigInteger(); + } catch (NumberFormatException e) { + return failConversion(val, columnType, typeString, BigInteger.class, e); + } + default: + } + return failConversion(val, columnType, typeString, BigInteger.class); + } + private static BigDecimal asBigDecimal(Object val, EsType columnType, String typeString) throws SQLException { switch (columnType) { case BOOLEAN: @@ -533,6 +580,8 @@ private static BigDecimal asBigDecimal(Object val, EsType columnType, String typ case INTEGER: case LONG: return BigDecimal.valueOf(((Number) val).longValue()); + case UNSIGNED_LONG: + return new BigDecimal(asBigInteger(val, columnType, typeString)); case FLOAT: case HALF_FLOAT: // floats are passed in as doubles here, so we need to dip into string to keep original float's (reduced) precision. @@ -573,28 +622,60 @@ private static OffsetDateTime asOffsetDateTime(Object val, EsType columnType, St throw new SQLFeatureNotSupportedException(); } - private static byte safeToByte(long x) throws SQLException { + private static byte safeToByte(Number n) throws SQLException { + if (n instanceof BigInteger) { + try { + return ((BigInteger) n).byteValueExact(); + } catch (ArithmeticException ae) { + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", n)); + } + } + long x = n.longValue(); if (x > Byte.MAX_VALUE || x < Byte.MIN_VALUE) { - throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", Long.toString(x))); + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", n)); } return (byte) x; } - private static short safeToShort(long x) throws SQLException { + private static short safeToShort(Number n) throws SQLException { + if (n instanceof BigInteger) { + try { + return ((BigInteger) n).shortValueExact(); + } catch (ArithmeticException ae) { + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", n)); + } + } + long x = n.longValue(); if (x > Short.MAX_VALUE || x < Short.MIN_VALUE) { - throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", Long.toString(x))); + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", n)); } return (short) x; } - private static int safeToInt(long x) throws SQLException { + private static int safeToInt(Number n) throws SQLException { + if (n instanceof BigInteger) { + try { + return ((BigInteger) n).intValueExact(); + } catch (ArithmeticException ae) { + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", n)); + } + } + long x = n.longValue(); if (x > Integer.MAX_VALUE || x < Integer.MIN_VALUE) { - throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", Long.toString(x))); + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", n)); } return (int) x; } - private static long safeToLong(double x) throws SQLException { + private static long safeToLong(Number n) throws SQLException { + if (n instanceof BigInteger) { + try { + return ((BigInteger) n).longValueExact(); + } catch (ArithmeticException ae) { + throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", n)); + } + } + double x = n.doubleValue(); if (x > Long.MAX_VALUE || x < Long.MIN_VALUE) { throw new SQLException(format(Locale.ROOT, "Numeric %s out of range", Double.toString(x))); } diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeUtils.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeUtils.java index cde126d79b4a8..c3be465e42b7e 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeUtils.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/TypeUtils.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.jdbc; +import java.math.BigInteger; import java.sql.JDBCType; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; @@ -25,6 +26,7 @@ import java.util.Map.Entry; import java.util.Set; +import static java.sql.Types.BIGINT; import static java.util.Collections.unmodifiableMap; final class TypeUtils { @@ -48,18 +50,21 @@ private TypeUtils() {} EsType.DATETIME ); + public static final int LONG_MAX_LENGTH = String.valueOf(Long.MAX_VALUE).length(); // type length value as defined in ES + static { + // Note: keep in sync with org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils#CLASS_TO_ES_TYPE Map, EsType> aMap = new LinkedHashMap<>(); aMap.put(Boolean.class, EsType.BOOLEAN); aMap.put(Byte.class, EsType.BYTE); aMap.put(Short.class, EsType.SHORT); aMap.put(Integer.class, EsType.INTEGER); aMap.put(Long.class, EsType.LONG); + aMap.put(BigInteger.class, EsType.UNSIGNED_LONG); aMap.put(Float.class, EsType.FLOAT); aMap.put(Double.class, EsType.DOUBLE); aMap.put(String.class, EsType.KEYWORD); aMap.put(byte[].class, EsType.BINARY); - aMap.put(String.class, EsType.KEYWORD); aMap.put(Timestamp.class, EsType.DATETIME); // apart from the mappings in {@code DataType} three more Java classes can be mapped to a {@code JDBCType.TIMESTAMP} @@ -78,6 +83,7 @@ private TypeUtils() {} types.put(EsType.SHORT, Short.class); types.put(EsType.INTEGER, Integer.class); types.put(EsType.LONG, Long.class); + types.put(EsType.UNSIGNED_LONG, BigInteger.class); types.put(EsType.DOUBLE, Double.class); types.put(EsType.FLOAT, Float.class); types.put(EsType.HALF_FLOAT, Double.class); @@ -155,6 +161,16 @@ static EsType of(int sqlType) throws SQLException { return dataType; } + static EsType of(SQLType sqlType, int scaleOrLength) throws SQLException { + EsType esType; + if (sqlType.getVendorTypeNumber() == BIGINT) { + esType = scaleOrLength > LONG_MAX_LENGTH ? EsType.UNSIGNED_LONG : EsType.LONG; + } else { + esType = TypeUtils.of(sqlType); + } + return esType; + } + static EsType of(String name) throws SQLException { EsType dataType = ENUM_NAME_TO_TYPE.get(name); if (dataType == null) { @@ -182,4 +198,8 @@ static EsType of(Class clazz) throws SQLException { } return dataType; } + + static int scaleOrLength(Object val) { + return val instanceof BigInteger ? LONG_MAX_LENGTH + 1 : 0; + } } diff --git a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatementTests.java b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatementTests.java index fce0b19da5a33..43a47611c79c9 100644 --- a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatementTests.java +++ b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/JdbcPreparedStatementTests.java @@ -9,8 +9,10 @@ import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.test.ESTestCase; +import java.math.BigInteger; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.sql.JDBCType; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.Struct; @@ -39,6 +41,7 @@ import static org.elasticsearch.xpack.sql.jdbc.EsType.LONG; import static org.elasticsearch.xpack.sql.jdbc.EsType.SHORT; import static org.elasticsearch.xpack.sql.jdbc.EsType.TIME; +import static org.elasticsearch.xpack.sql.jdbc.EsType.UNSIGNED_LONG; public class JdbcPreparedStatementTests extends ESTestCase { @@ -122,6 +125,14 @@ public void testSettingByteTypeValues() throws SQLException { assertEquals(123, value(jps)); assertEquals(INTEGER, jdbcType(jps)); + jps.setObject(1, (byte) 123, Types.BIGINT); + assertEquals(123L, value(jps)); + assertEquals(LONG, jdbcType(jps)); + + jps.setObject(1, (byte) 123, Types.BIGINT, 20); + assertEquals(BigInteger.valueOf(123), value(jps)); + assertEquals(UNSIGNED_LONG, jdbcType(jps)); + jps.setObject(1, (byte) -128, Types.DOUBLE); assertEquals(-128.0, value(jps)); assertEquals(DOUBLE, jdbcType(jps)); @@ -234,6 +245,55 @@ public void testSettingLongValues() throws SQLException { assertEquals(HALF_FLOAT, jdbcType(jps)); } + public void testSettingBigIntegerValues() throws SQLException { + JdbcPreparedStatement jps = createJdbcPreparedStatement(); + + BigInteger bi = BigInteger.valueOf(randomLong()).abs(); + + jps.setObject(1, bi); + assertEquals(bi, value(jps)); + assertEquals(UNSIGNED_LONG, jdbcType(jps)); + assertTrue(value(jps) instanceof BigInteger); + + jps.setObject(1, bi, Types.VARCHAR); + assertEquals(String.valueOf(bi), value(jps)); + assertEquals(KEYWORD, jdbcType(jps)); + + jps.setObject(1, bi, Types.BIGINT); + assertEquals(bi.longValueExact(), value(jps)); + assertEquals(LONG, jdbcType(jps)); + + jps.setObject(1, bi, Types.DOUBLE); + assertEquals(bi.doubleValue(), value(jps)); + assertEquals(DOUBLE, jdbcType(jps)); + + jps.setObject(1, bi, Types.FLOAT); + assertEquals(bi.doubleValue(), value(jps)); + assertEquals(HALF_FLOAT, jdbcType(jps)); + + jps.setObject(1, bi, Types.REAL); + assertEquals(bi.floatValue(), value(jps)); + assertEquals(FLOAT, jdbcType(jps)); + + jps.setObject(1, BigInteger.ZERO, Types.BOOLEAN); + assertEquals(false, value(jps)); + assertEquals(BOOLEAN, jdbcType(jps)); + + jps.setObject(1, BigInteger.TEN, Types.BOOLEAN); + assertEquals(true, value(jps)); + assertEquals(BOOLEAN, jdbcType(jps)); + + jps.setObject(1, bi.longValueExact(), JDBCType.BIGINT, 19); + assertTrue(value(jps) instanceof Long); + assertEquals(bi.longValueExact(), value(jps)); + assertEquals(LONG, jdbcType(jps)); + + jps.setObject(1, bi.longValueExact(), JDBCType.BIGINT, 20); + assertTrue(value(jps) instanceof BigInteger); + assertEquals(bi, value(jps)); + assertEquals(UNSIGNED_LONG, jdbcType(jps)); + } + public void testThrownExceptionsWhenSettingLongValues() throws SQLException { JdbcPreparedStatement jps = createJdbcPreparedStatement(); long someLong = randomLong(); diff --git a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/TypeConverterTests.java b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/TypeConverterTests.java index 137d9e4dacc33..f48e635f6a752 100644 --- a/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/TypeConverterTests.java +++ b/x-pack/plugin/sql/jdbc/src/test/java/org/elasticsearch/xpack/sql/jdbc/TypeConverterTests.java @@ -12,6 +12,8 @@ import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.json.JsonXContent; +import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.Date; import java.sql.Timestamp; import java.time.ZoneId; @@ -24,6 +26,32 @@ public class TypeConverterTests extends ESTestCase { private static final ZoneId UTC = ZoneId.of("Z"); + public void testConvertToBigInteger() throws Exception { + { + assertEquals(BigInteger.ZERO, convertAsNative(false, BigInteger.class)); + assertEquals(BigInteger.ONE, convertAsNative(true, BigInteger.class)); + } + { + BigInteger bi = randomBigInteger(); + assertEquals(bi, convertAsNative(bi, BigInteger.class)); + } + // 18446744073709551615 + BigInteger UNSIGNED_LONG_MAX = BigInteger.ONE.shiftLeft(Long.SIZE).subtract(BigInteger.ONE); + BigDecimal bd = BigDecimal.valueOf(randomDouble()).abs().remainder(new BigDecimal(UNSIGNED_LONG_MAX)); + { + float f = bd.floatValue(); + assertEquals(BigDecimal.valueOf(f).toBigInteger(), convertAsNative(f, BigInteger.class)); + } + { + double d = bd.doubleValue(); + assertEquals(BigDecimal.valueOf(d).toBigInteger(), convertAsNative(d, BigInteger.class)); + } + { + String s = bd.toString(); + assertEquals(new BigDecimal(s).toBigInteger(), convertAsNative(s, BigInteger.class)); + } + } + public void testFloatAsNative() throws Exception { assertThat(convertAsNative(42.0f, EsType.FLOAT), instanceOf(Float.class)); assertThat(convertAsNative(42.0, EsType.FLOAT), instanceOf(Float.class)); @@ -61,15 +89,25 @@ public void testDateAsNative() throws Exception { assertEquals(now.toLocalDate().atStartOfDay(ZoneId.of("Etc/GMT-10")).toInstant().toEpochMilli(), ((Date) nativeObject).getTime()); } - private Object convertAsNative(Object value, EsType type) throws Exception { - // Simulate sending over XContent + // Simulate sending over XContent + private static Object throughXContent(Object value) throws Exception { XContentBuilder builder = JsonXContent.contentBuilder(); builder.startObject(); builder.field("value"); builder.value(value); builder.endObject(); builder.close(); - Object copy = XContentHelper.convertToMap(BytesReference.bytes(builder), false, builder.contentType()).v2().get("value"); + return XContentHelper.convertToMap(BytesReference.bytes(builder), false, builder.contentType()).v2().get("value"); + + } + + private Object convertAsNative(Object value, EsType type) throws Exception { + Object copy = throughXContent(value); return TypeConverter.convert(copy, type, type.toString()); } + + private Object convertAsNative(Object value, Class nativeType) throws Exception { + EsType esType = TypeUtils.of(value.getClass()); + return TypeConverter.convert(value, esType, nativeType, esType.toString()); + } } diff --git a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java index 7d11bfb113bad..120081a7edf7f 100644 --- a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java +++ b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java @@ -7,17 +7,26 @@ package org.elasticsearch.xpack.sql.qa.jdbc; import org.elasticsearch.Version; +import org.elasticsearch.xpack.sql.jdbc.EsType; import org.elasticsearch.xpack.sql.proto.StringUtils; +import java.math.BigInteger; import java.sql.Date; import java.sql.Time; +import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Calendar; +import java.util.Collections; +import java.util.GregorianCalendar; +import java.util.LinkedHashMap; +import java.util.Map; +import static org.elasticsearch.Version.V_8_2_0; import static org.elasticsearch.common.time.DateUtils.toMilliSeconds; import static org.elasticsearch.test.ESTestCase.randomLongBetween; @@ -25,11 +34,16 @@ final class JdbcTestUtils { private JdbcTestUtils() {} + private static final Map, EsType> CLASS_TO_ES_TYPE; + static final ZoneId UTC = ZoneId.of("Z"); static final String JDBC_TIMEZONE = "timezone"; private static final String DRIVER_VERSION_PROPERTY_NAME = "jdbc.driver.version"; static final LocalDate EPOCH = LocalDate.of(1970, 1, 1); + static final String UNSIGNED_LONG_TYPE_NAME = "UNSIGNED_LONG"; + static final BigInteger UNSIGNED_LONG_MAX = BigInteger.ONE.shiftLeft(Long.SIZE).subtract(BigInteger.ONE); + /* * The version of the driver that the QA (bwc-)tests run against. * Note: when adding a version-gated feature (i.e. new feature that would not be supported by old drivers) and adding code in these QA @@ -50,6 +64,36 @@ private JdbcTestUtils() {} // master's version is x.0.0-SNAPSHOT, tho Version#fromString() won't accept that back for recent versions String jdbcDriverVersion = System.getProperty(DRIVER_VERSION_PROPERTY_NAME, "").replace("-SNAPSHOT", ""); JDBC_DRIVER_VERSION = Version.fromString(jdbcDriverVersion); // takes empty and null strings, resolves them to CURRENT + + // Note: keep in sync with org.elasticsearch.xpack.sql.jdbc.TypeUtils#CLASS_TO_TYPE + Map, EsType> aMap = new LinkedHashMap<>(); + aMap.put(Boolean.class, EsType.BOOLEAN); + aMap.put(Byte.class, EsType.BYTE); + aMap.put(Short.class, EsType.SHORT); + aMap.put(Integer.class, EsType.INTEGER); + aMap.put(Long.class, EsType.LONG); + if (isUnsignedLongSupported()) { + aMap.put(BigInteger.class, EsType.UNSIGNED_LONG); + } + aMap.put(Float.class, EsType.FLOAT); + aMap.put(Double.class, EsType.DOUBLE); + aMap.put(String.class, EsType.KEYWORD); + aMap.put(byte[].class, EsType.BINARY); + aMap.put(Timestamp.class, EsType.DATETIME); + + // apart from the mappings in {@code DataType} three more Java classes can be mapped to a {@code JDBCType.TIMESTAMP} + // according to B-4 table from the jdbc4.2 spec + aMap.put(Calendar.class, EsType.DATETIME); + aMap.put(GregorianCalendar.class, EsType.DATETIME); + aMap.put(java.util.Date.class, EsType.DATETIME); + aMap.put(java.sql.Date.class, EsType.DATETIME); + aMap.put(java.sql.Time.class, EsType.TIME); + aMap.put(LocalDateTime.class, EsType.DATETIME); + CLASS_TO_ES_TYPE = Collections.unmodifiableMap(aMap); + } + + static EsType of(Class clazz) { + return CLASS_TO_ES_TYPE.get(clazz); } static String of(long millis, String zoneId) { @@ -107,4 +151,8 @@ static int extractNanosOnly(long nanos) { static boolean versionSupportsDateNanos() { return JDBC_DRIVER_VERSION.onOrAfter(Version.V_7_12_0); } + + public static boolean isUnsignedLongSupported() { + return JDBC_DRIVER_VERSION.onOrAfter(V_8_2_0); + } } diff --git a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/PreparedStatementTestCase.java b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/PreparedStatementTestCase.java index b026cc856ac24..b2b983803260c 100644 --- a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/PreparedStatementTestCase.java +++ b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/PreparedStatementTestCase.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.Connection; import java.sql.Date; import java.sql.JDBCType; @@ -39,7 +40,9 @@ import static org.elasticsearch.xpack.sql.jdbc.EsType.LONG; import static org.elasticsearch.xpack.sql.jdbc.EsType.SHORT; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_DRIVER_VERSION; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.UNSIGNED_LONG_TYPE_NAME; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.extractNanosOnly; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.isUnsignedLongSupported; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.randomTimeInNanos; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.versionSupportsDateNanos; import static org.hamcrest.Matchers.equalTo; @@ -411,6 +414,7 @@ public void testSingleParameterMultipleTypes() throws SQLException { String stringVal = randomAlphaOfLength(randomIntBetween(0, 1000)); int intVal = randomInt(); long longVal = randomLong(); + BigInteger bigIntegerVal = randomBigInteger(); double doubleVal = randomDouble(); float floatVal = randomFloat(); boolean booleanVal = randomBoolean(); @@ -442,6 +446,26 @@ public void testSingleParameterMultipleTypes() throws SQLException { } } + public void testSingleParameterUnsignedLong() throws SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + BigInteger bigIntegerVal = randomBigInteger(); + long longVal = randomLong(); + + try (Connection connection = esJdbc()) { + try (PreparedStatement statement = connection.prepareStatement("SELECT ?")) { + + statement.setObject(1, bigIntegerVal); + assertEquals(new Tuple<>(UNSIGNED_LONG_TYPE_NAME, bigIntegerVal), execute(statement)); + + statement.setObject(1, longVal, JDBCType.BIGINT, 19); + assertEquals(new Tuple<>(LONG.getName(), longVal), execute(statement)); + statement.setObject(1, Math.abs(longVal), JDBCType.BIGINT, 20); + assertEquals(new Tuple<>(UNSIGNED_LONG_TYPE_NAME, BigInteger.valueOf(Math.abs(longVal))), execute(statement)); + } + } + } + private Tuple execute(PreparedStatement statement) throws SQLException { try (ResultSet results = statement.executeQuery()) { ResultSetMetaData resultSetMetaData = results.getMetaData(); diff --git a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetMetaDataTestCase.java b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetMetaDataTestCase.java index b9ff4b53cc7c2..8c8afc4550254 100644 --- a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetMetaDataTestCase.java +++ b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetMetaDataTestCase.java @@ -15,10 +15,19 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; + +import static java.util.Collections.singletonList; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_DRIVER_VERSION; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.UNSIGNED_LONG_TYPE_NAME; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.isUnsignedLongSupported; public abstract class ResultSetMetaDataTestCase extends JdbcIntegrationTestCase { - private final String[] fieldsNames = new String[] { + private static final List FIELDS_NAMES = List.of( "test_byte", "test_integer", "test_long", @@ -27,40 +36,74 @@ public abstract class ResultSetMetaDataTestCase extends JdbcIntegrationTestCase "test_float", "test_keyword", "test_boolean", - "test_date" }; + "test_date" + ); + private static final String UNSIGNED_LONG_FIELD = "test_" + UNSIGNED_LONG_TYPE_NAME.toLowerCase(Locale.ROOT); - public void testValidGetObjectCalls() throws IOException, SQLException { + private static void createMappedIndex(List fieldsNames) throws IOException { ResultSetTestCase.createIndex("test"); ResultSetTestCase.updateMapping("test", builder -> { for (String field : fieldsNames) { builder.startObject(field).field("type", field.substring(5)).endObject(); } }); + } + + public void testValidGetObjectCalls() throws IOException, SQLException { + doTestValidGetObjectCalls(FIELDS_NAMES); + } - String q = "SELECT test_byte, test_integer, test_long, test_short, test_double, test_float, test_keyword, " - + "test_boolean, test_date FROM test"; + public void testValidGetObjectCallsWithUnsignedLong() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + doTestValidGetObjectCalls(singletonList(UNSIGNED_LONG_FIELD)); + } + + private void doTestValidGetObjectCalls(List fieldsNames) throws IOException, SQLException { + createMappedIndex(fieldsNames); + + String q = "SELECT " + String.join(", ", fieldsNames) + " FROM test"; doWithQuery(q, r -> assertColumnNamesAndLabels(r.getMetaData(), fieldsNames)); - q = "SELECT test_byte AS b, test_integer AS i, test_long AS l, test_short AS s, test_double AS d, test_float AS f, " - + "test_keyword AS k, test_boolean AS bool, test_date AS dt FROM test"; - doWithQuery(q, r -> assertColumnNamesAndLabels(r.getMetaData(), new String[] { "b", "i", "l", "s", "d", "f", "k", "bool", "dt" })); + String selectedFields = fieldsNames.stream().map(x -> x + " AS " + x.replace("_", "")).collect(Collectors.joining(", ")); + q = "SELECT " + selectedFields + " FROM test"; + doWithQuery( + q, + r -> assertColumnNamesAndLabels(r.getMetaData(), fieldsNames.stream().map(x -> x.replace("_", "")).collect(Collectors.toList())) + ); + } + + public void testUnsignedLongConditionallyReturnedOnStarExpansion() throws IOException, SQLException { + List fieldsNames = new ArrayList<>(FIELDS_NAMES); + fieldsNames.add(UNSIGNED_LONG_FIELD); + createMappedIndex(fieldsNames); + + String query = "SELECT * FROM test"; + doWithQuery(query, r -> { + List columnTypeNames = new ArrayList<>(fieldsNames.size()); + for (int i = 0; i < r.getMetaData().getColumnCount(); i++) { + columnTypeNames.add(r.getMetaData().getColumnTypeName(i + 1).toLowerCase(Locale.ROOT)); + } + // the assert executes only if UL is supported; the failing case would be triggered earlier in the driver already + assertEquals(isUnsignedLongSupported(), columnTypeNames.contains(UNSIGNED_LONG_TYPE_NAME.toLowerCase(Locale.ROOT))); + }); } private void doWithQuery(String query, CheckedConsumer consumer) throws SQLException { try (Connection connection = esJdbc()) { try (PreparedStatement statement = connection.prepareStatement(query)) { try (ResultSet results = statement.executeQuery()) { - assertEquals(fieldsNames.length, results.getMetaData().getColumnCount()); consumer.accept(results); } } } } - private void assertColumnNamesAndLabels(ResultSetMetaData metaData, String[] names) throws SQLException { - for (int i = 0; i < fieldsNames.length; i++) { - assertEquals(names[i], metaData.getColumnName(i + 1)); - assertEquals(names[i], metaData.getColumnLabel(i + 1)); + private void assertColumnNamesAndLabels(ResultSetMetaData metaData, List names) throws SQLException { + assertEquals(names.size(), metaData.getColumnCount()); + for (int i = 0; i < names.size(); i++) { + assertEquals(names.get(i), metaData.getColumnName(i + 1)); + assertEquals(names.get(i), metaData.getColumnLabel(i + 1)); } } } diff --git a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java index ca3b8216cf1b1..7aafcce432a52 100644 --- a/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java +++ b/x-pack/plugin/sql/qa/jdbc/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java @@ -24,6 +24,7 @@ import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.Blob; import java.sql.Clob; import java.sql.Connection; @@ -46,19 +47,16 @@ import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; -import java.util.Set; import java.util.TimeZone; import java.util.function.Supplier; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.Stream; import static java.lang.String.format; import static java.util.Calendar.DAY_OF_MONTH; @@ -69,14 +67,18 @@ import static java.util.Calendar.MONTH; import static java.util.Calendar.SECOND; import static java.util.Calendar.YEAR; +import static java.util.Collections.singletonList; import static java.util.regex.Pattern.compile; import static java.util.regex.Pattern.quote; import static org.elasticsearch.common.time.DateUtils.toMilliSeconds; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_DRIVER_VERSION; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_TIMEZONE; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.UNSIGNED_LONG_MAX; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.UNSIGNED_LONG_TYPE_NAME; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.asDate; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.asTime; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.extractNanosOnly; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.isUnsignedLongSupported; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.of; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.randomTimeInNanos; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.versionSupportsDateNanos; @@ -84,7 +86,7 @@ public abstract class ResultSetTestCase extends JdbcIntegrationTestCase { - static final Set fieldsNames = Stream.of( + static final List FIELDS_NAMES = List.of( "test_byte", "test_integer", "test_long", @@ -92,12 +94,16 @@ public abstract class ResultSetTestCase extends JdbcIntegrationTestCase { "test_double", "test_float", "test_keyword" - ).collect(Collectors.toCollection(HashSet::new)); + ); + static final String UNSIGNED_LONG_FIELD = "test_" + UNSIGNED_LONG_TYPE_NAME.toLowerCase(Locale.ROOT); + static final Map, SQLType> dateTimeTestingFields = new HashMap<>(); - static final String SELECT_ALL_FIELDS = "SELECT test_boolean, test_byte, test_integer," - + "test_long, test_short, test_double, test_float, test_keyword, test_date, test_date_nanos FROM test"; + static final String SELECT_ALL_FIELDS; static final String SELECT_WILDCARD = "SELECT * FROM test"; + static { + SELECT_ALL_FIELDS = "SELECT test_boolean, " + String.join(", ", FIELDS_NAMES) + ", test_date, test_date_nanos FROM test"; + dateTimeTestingFields.put(new Tuple<>("test_boolean", true), EsType.BOOLEAN); dateTimeTestingFields.put(new Tuple<>("test_byte", 1), EsType.BYTE); dateTimeTestingFields.put(new Tuple<>("test_integer", 1), EsType.INTEGER); @@ -259,15 +265,7 @@ public void testGettingInvalidByte() throws IOException, SQLException { ? Double.toString(doubleNotByte) : Long.toString(Math.round(doubleNotByte)); - index("test", "1", builder -> { - builder.field("test_integer", intNotByte); - builder.field("test_long", longNotByte); - builder.field("test_short", shortNotByte); - builder.field("test_double", doubleNotByte); - builder.field("test_float", floatNotByte); - builder.field("test_keyword", randomString); - builder.field("test_date", randomDate); - }); + indexTestFieldsDoc("1", intNotByte, longNotByte, shortNotByte, doubleNotByte, floatNotByte, randomString, new Date(randomDate)); doWithQuery(SELECT_WILDCARD, results -> { results.next(); @@ -315,6 +313,25 @@ public void testGettingInvalidByte() throws IOException, SQLException { }); } + public void testGettingInvalidByteFromUnsignedLong() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + createIndex("test"); + updateMappingForNumericValuesTests("test", singletonList(UNSIGNED_LONG_FIELD)); + + BigInteger bigIntegerNotByte = BigInteger.valueOf(randomLongBetween(Byte.MAX_VALUE + 1, Long.MAX_VALUE)); + indexTestFieldsDoc("1", bigIntegerNotByte); + + doWithQuery(SELECT_WILDCARD, results -> { + results.next(); + + SQLException sqle = expectThrows(SQLException.class, () -> results.getByte(UNSIGNED_LONG_FIELD)); + assertEquals(format(Locale.ROOT, "Numeric %s out of range", bigIntegerNotByte), sqle.getMessage()); + sqle = expectThrows(SQLException.class, () -> results.getObject(UNSIGNED_LONG_FIELD, Byte.class)); + assertEquals(format(Locale.ROOT, "Numeric %s out of range", bigIntegerNotByte), sqle.getMessage()); + }); + } + // Short values testing public void testGettingValidShortWithoutCasting() throws IOException, SQLException { List shortTestValues = createTestDataForNumericValueTests(ESTestCase::randomShort); @@ -373,10 +390,7 @@ public void testGettingValidShortWithCasting() throws IOException, SQLException public void testGettingInvalidShort() throws IOException, SQLException { createIndex("test"); updateMappingForNumericValuesTests("test"); - updateMapping("test", builder -> { - builder.startObject("test_keyword").field("type", "keyword").endObject(); - builder.startObject("test_date").field("type", "date").endObject(); - }); + updateMapping("test", builder -> { builder.startObject("test_date").field("type", "date").endObject(); }); int intNotShort = randomIntBetween(Short.MAX_VALUE + 1, Integer.MAX_VALUE); long longNotShort = randomLongBetween(Short.MAX_VALUE + 1, Long.MAX_VALUE); @@ -389,14 +403,7 @@ public void testGettingInvalidShort() throws IOException, SQLException { ? Double.toString(doubleNotShort) : Long.toString(Math.round(doubleNotShort)); - index("test", "1", builder -> { - builder.field("test_integer", intNotShort); - builder.field("test_long", longNotShort); - builder.field("test_double", doubleNotShort); - builder.field("test_float", floatNotShort); - builder.field("test_keyword", randomString); - builder.field("test_date", randomDate); - }); + indexTestFieldsDoc("1", intNotShort, longNotShort, doubleNotShort, floatNotShort, randomString, new Date(randomDate)); doWithQuery(SELECT_WILDCARD, results -> { results.next(); @@ -439,6 +446,25 @@ public void testGettingInvalidShort() throws IOException, SQLException { }); } + public void testGettingInvalidShortFromUnsignedLong() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + createIndex("test"); + updateMappingForNumericValuesTests("test", singletonList(UNSIGNED_LONG_FIELD)); + + BigInteger bigIntegerNotShort = BigInteger.valueOf(randomLongBetween(Short.MAX_VALUE + 1, Long.MAX_VALUE)); + indexTestFieldsDoc("1", bigIntegerNotShort); + + doWithQuery(SELECT_WILDCARD, results -> { + results.next(); + + SQLException sqle = expectThrows(SQLException.class, () -> results.getShort(UNSIGNED_LONG_FIELD)); + assertEquals(format(Locale.ROOT, "Numeric %s out of range", bigIntegerNotShort), sqle.getMessage()); + sqle = expectThrows(SQLException.class, () -> results.getObject(UNSIGNED_LONG_FIELD, Short.class)); + assertEquals(format(Locale.ROOT, "Numeric %s out of range", bigIntegerNotShort), sqle.getMessage()); + }); + } + // Integer values testing public void testGettingValidIntegerWithoutCasting() throws IOException, SQLException { List integerTestValues = createTestDataForNumericValueTests(ESTestCase::randomInt); @@ -497,10 +523,7 @@ public void testGettingValidIntegerWithCasting() throws IOException, SQLExceptio public void testGettingInvalidInteger() throws IOException, SQLException { createIndex("test"); updateMappingForNumericValuesTests("test"); - updateMapping("test", builder -> { - builder.startObject("test_keyword").field("type", "keyword").endObject(); - builder.startObject("test_date").field("type", "date").endObject(); - }); + updateMapping("test", builder -> { builder.startObject("test_date").field("type", "date").endObject(); }); long longNotInt = randomLongBetween(getMaxIntPlusOne(), Long.MAX_VALUE); double doubleNotInt = randomDoubleBetween(getMaxIntPlusOne().doubleValue(), Double.MAX_VALUE, true); @@ -512,13 +535,7 @@ public void testGettingInvalidInteger() throws IOException, SQLException { ? Double.toString(doubleNotInt) : Long.toString(Math.round(doubleNotInt)); - index("test", "1", builder -> { - builder.field("test_long", longNotInt); - builder.field("test_double", doubleNotInt); - builder.field("test_float", floatNotInt); - builder.field("test_keyword", randomString); - builder.field("test_date", randomDate); - }); + indexTestFieldsDoc("1", longNotInt, doubleNotInt, floatNotInt, randomString, new Date(randomDate)); doWithQuery(SELECT_WILDCARD, results -> { results.next(); @@ -556,6 +573,25 @@ public void testGettingInvalidInteger() throws IOException, SQLException { }); } + public void testGettingInvalidIntegerFromUnsignedLong() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + createIndex("test"); + updateMappingForNumericValuesTests("test", singletonList(UNSIGNED_LONG_FIELD)); + + BigInteger bigIntegerNotInt = BigInteger.valueOf(randomLongBetween(getMaxIntPlusOne(), Long.MAX_VALUE)); + indexTestFieldsDoc("1", bigIntegerNotInt); + + doWithQuery(SELECT_WILDCARD, results -> { + results.next(); + + SQLException sqle = expectThrows(SQLException.class, () -> results.getInt(UNSIGNED_LONG_FIELD)); + assertEquals(format(Locale.ROOT, "Numeric %s out of range", bigIntegerNotInt), sqle.getMessage()); + sqle = expectThrows(SQLException.class, () -> results.getObject(UNSIGNED_LONG_FIELD, Integer.class)); + assertEquals(format(Locale.ROOT, "Numeric %s out of range", bigIntegerNotInt), sqle.getMessage()); + }); + } + // Long values testing public void testGettingValidLongWithoutCasting() throws IOException, SQLException { List longTestValues = createTestDataForNumericValueTests(ESTestCase::randomLong); @@ -611,22 +647,14 @@ public void testGettingValidLongWithCasting() throws IOException, SQLException { public void testGettingInvalidLong() throws IOException, SQLException { createIndex("test"); updateMappingForNumericValuesTests("test"); - updateMapping("test", builder -> { - builder.startObject("test_keyword").field("type", "keyword").endObject(); - builder.startObject("test_date").field("type", "date").endObject(); - }); + updateMapping("test", builder -> { builder.startObject("test_date").field("type", "date").endObject(); }); double doubleNotLong = randomDoubleBetween(getMaxLongPlusOne(), Double.MAX_VALUE, true); float floatNotLong = randomFloatBetween(getMaxLongPlusOne().floatValue(), Float.MAX_VALUE); String randomString = randomUnicodeOfCodepointLengthBetween(128, 256); long randomDate = randomMillisUpToYear9999(); - index("test", "1", builder -> { - builder.field("test_double", doubleNotLong); - builder.field("test_float", floatNotLong); - builder.field("test_keyword", randomString); - builder.field("test_date", randomDate); - }); + indexTestFieldsDoc("1", doubleNotLong, floatNotLong, randomString, new Date(randomDate)); doWithQuery(SELECT_WILDCARD, results -> { results.next(); @@ -659,6 +687,163 @@ public void testGettingInvalidLong() throws IOException, SQLException { }); } + public void testGettingInvalidLongFromUnsignedLong() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + createIndex("test"); + updateMappingForNumericValuesTests("test", singletonList(UNSIGNED_LONG_FIELD)); + + BigInteger bigIntegerNotLong = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.valueOf(randomNonNegativeLong())); + indexTestFieldsDoc("1", bigIntegerNotLong); + + doWithQuery(SELECT_WILDCARD, results -> { + results.next(); + + SQLException sqle = expectThrows(SQLException.class, () -> results.getLong(UNSIGNED_LONG_FIELD)); + assertEquals(format(Locale.ROOT, "Numeric %s out of range", bigIntegerNotLong), sqle.getMessage()); + sqle = expectThrows(SQLException.class, () -> results.getObject(UNSIGNED_LONG_FIELD, Long.class)); + assertEquals(format(Locale.ROOT, "Numeric %s out of range", bigIntegerNotLong), sqle.getMessage()); + }); + } + + // BigInteger/unsigned_long values testing + public void testGettingValidBigIntegerWithoutCasting() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + List bigIntegerTestValues = createTestDataForNumericValueTests(ESTestCase::randomBigInteger); + BigInteger random1 = bigIntegerTestValues.get(0); + BigInteger random2 = bigIntegerTestValues.get(1); + BigInteger random3 = bigIntegerTestValues.get(2); + + doWithQuery("SELECT test_unsigned_long, test_null_unsigned_long, test_keyword FROM test", results -> { + ResultSetMetaData resultSetMetaData = results.getMetaData(); + + results.next(); + assertEquals(3, resultSetMetaData.getColumnCount()); + assertEquals(UNSIGNED_LONG_TYPE_NAME, resultSetMetaData.getColumnTypeName(1)); + assertEquals(random1, results.getObject(1)); + assertEquals(random1, results.getObject(UNSIGNED_LONG_FIELD)); + assertEquals(random1, results.getObject(UNSIGNED_LONG_FIELD, BigInteger.class)); + assertTrue(results.getObject(1) instanceof BigInteger); + + assertEquals(UNSIGNED_LONG_TYPE_NAME, resultSetMetaData.getColumnTypeName(2)); + assertNull(results.getObject(2)); + assertTrue(results.wasNull()); + assertNull(results.getObject("test_null_unsigned_long")); + assertTrue(results.wasNull()); + + assertTrue(results.next()); + assertEquals(random2, results.getObject(1)); + assertEquals(random2, results.getObject(UNSIGNED_LONG_FIELD)); + assertTrue(results.getObject(1) instanceof BigInteger); + assertEquals(random3.toString(), results.getObject("test_keyword")); + + assertFalse(results.next()); + }); + } + + public void testGettingValidBigIntegerWithCasting() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + Map map = createTestDataForNumericValueTypes(ESTestCase::randomBigInteger); + updateMappingForNumericValuesTests("test", singletonList(UNSIGNED_LONG_FIELD)); + + BigInteger randomBigInteger = randomBigInteger(); + indexTestFieldsDoc("2", randomBigInteger); + + doWithQuery(SELECT_WILDCARD, results -> { + results.next(); + for (Entry e : map.entrySet()) { + BigInteger actual = results.getObject(e.getKey(), BigInteger.class); + if (e.getValue() instanceof Float) { + assertEquals("For field " + e.getKey(), e.getValue(), actual.floatValue()); + } else if (e.getValue() instanceof Double) { + assertEquals("For field " + e.getKey(), e.getValue(), actual.doubleValue()); + } else { + assertEquals("For field " + e.getKey(), e.getValue().longValue(), results.getLong(e.getKey())); + assertEquals("For field " + e.getKey(), e.getValue().longValue(), actual.longValue()); + } + } + + results.next(); + BigInteger actual = results.getObject(UNSIGNED_LONG_FIELD, BigInteger.class); + assertEquals("For field " + UNSIGNED_LONG_FIELD, randomBigInteger, actual); + }); + } + + public void testGettingInvalidBigInteger() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + createIndex("test"); + updateMappingForNumericValuesTests("test"); + updateMapping("test", builder -> { + builder.startObject("test_keyword").field("type", "keyword").endObject(); + builder.startObject("test_date").field("type", "date").endObject(); + }); + + String randomString = randomUnicodeOfCodepointLengthBetween(128, 256); + long randomDate = randomMillisUpToYear9999(); + + index("test", "1", builder -> { + builder.field("test_keyword", randomString); + builder.field("test_date", randomDate); + }); + + doWithQuery(SELECT_WILDCARD, results -> { + results.next(); + + SQLException sqle = expectThrows(SQLException.class, () -> results.getObject("test_keyword", BigInteger.class)); + assertEquals( + format(Locale.ROOT, "Unable to convert value [%.128s] of type [KEYWORD] to [BigInteger]", randomString), + sqle.getMessage() + ); + + sqle = expectThrows(SQLException.class, () -> results.getObject("test_date", BigInteger.class)); + assertEquals( + format(Locale.ROOT, "Unable to convert value [%.128s] of type [DATETIME] to [BigInteger]", asDateString(randomDate)), + sqle.getMessage() + ); + }); + } + + public void testGettingValidNumbersWithCastingFromUnsignedLong() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + createIndex("test"); + updateMappingForNumericValuesTests("test", singletonList(UNSIGNED_LONG_FIELD)); + + byte randomNonNegativeByte = randomNonNegativeByte(); + short randomNonNegativeShort = (short) (Math.abs(randomShort()) - 1); + int randomNonNegativeInt = Math.abs(randomInt()) - 1; + long randomNonNegativeLong = randomNonNegativeLong(); + double randomNonNegativeFloat = (float) randomDoubleBetween(0, UNSIGNED_LONG_MAX.doubleValue(), true); + double randomNonNegativeDouble = randomDoubleBetween(0, UNSIGNED_LONG_MAX.doubleValue(), true); + + int docId = 1; + indexTestFieldsDoc(String.valueOf(docId++), BigInteger.valueOf(randomNonNegativeByte)); + indexTestFieldsDoc(String.valueOf(docId++), BigInteger.valueOf(randomNonNegativeShort)); + indexTestFieldsDoc(String.valueOf(docId++), BigInteger.valueOf(randomNonNegativeInt)); + indexTestFieldsDoc(String.valueOf(docId++), BigInteger.valueOf(randomNonNegativeLong)); + indexTestFieldsDoc(String.valueOf(docId++), BigDecimal.valueOf(randomNonNegativeFloat).toBigInteger()); + indexTestFieldsDoc(String.valueOf(docId++), BigDecimal.valueOf(randomNonNegativeDouble).toBigInteger()); + + doWithQuery(SELECT_WILDCARD, results -> { + results.next(); + assertEquals(randomNonNegativeByte, results.getByte(1)); + results.next(); + assertEquals(randomNonNegativeShort, results.getShort(1)); + results.next(); + assertEquals(randomNonNegativeInt, results.getInt(1)); + results.next(); + assertEquals(randomNonNegativeLong, results.getLong(1)); + results.next(); + assertEquals(randomNonNegativeFloat, results.getFloat(1), 0f); + results.next(); + assertEquals(randomNonNegativeDouble, results.getDouble(1), 0d); + }); + + } + // Double values testing public void testGettingValidDoubleWithoutCasting() throws IOException, SQLException { List doubleTestValues = createTestDataForNumericValueTests(ESTestCase::randomDouble); @@ -1052,52 +1237,35 @@ public void testGettingBooleanValues() throws Exception { long randomDateNanos2 = randomTimeInNanos(); // true values - indexSimpleDocumentWithTrueValues(randomDate1, randomDateNanos1); - + indexSimpleDocumentWithBooleanValues("1", true, randomDate1, randomDateNanos1); // false values - index("test", "2", builder -> { - builder.field("test_boolean", false); - builder.field("test_byte", 0); - builder.field("test_integer", 0); - builder.field("test_long", 0L); - builder.field("test_short", 0); - builder.field("test_double", 0d); - builder.field("test_float", 0f); - builder.field("test_keyword", "false"); - builder.field("test_date", randomDate2); - builder.field("test_date_nanos", asTimestampWithNanos(randomDateNanos2)); - }); + indexSimpleDocumentWithBooleanValues("2", false, randomDate2, randomDateNanos2); // other (non 0 = true) values - index("test", "3", builder -> { - builder.field("test_byte", randomValueOtherThan((byte) 0, ESTestCase::randomByte)); - builder.field("test_integer", randomValueOtherThan(0, ESTestCase::randomInt)); - builder.field("test_long", randomValueOtherThan(0L, ESTestCase::randomLong)); - builder.field("test_short", randomValueOtherThan((short) 0, ESTestCase::randomShort)); - builder.field( - "test_double", - randomValueOtherThanMany( - i -> i < 1.0d && i > -1.0d && i < Double.MAX_VALUE && i > Double.MIN_VALUE, - () -> randomDouble() * randomInt() - ) - ); - builder.field( - "test_float", - randomValueOtherThanMany( - i -> i < 1.0f && i > -1.0f && i < Float.MAX_VALUE && i > Float.MIN_VALUE, - () -> randomFloat() * randomInt() - ) - ); - builder.field("test_keyword", "1"); - }); + indexTestFieldsDoc( + "3", + randomValueOtherThan((byte) 0, ESTestCase::randomByte), + randomValueOtherThan(0, ESTestCase::randomInt), + randomValueOtherThan(0L, ESTestCase::randomLong), + randomValueOtherThan((short) 0, ESTestCase::randomShort), + randomValueOtherThanMany( + i -> i < 1.0d && i > -1.0d && i < Double.MAX_VALUE && i > Double.MIN_VALUE, + () -> randomDouble() * randomInt() + ), + randomValueOtherThanMany( + i -> i < 1.0f && i > -1.0f && i < Float.MAX_VALUE && i > Float.MIN_VALUE, + () -> randomFloat() * randomInt() + ), + "1" + ); // other false values index("test", "4", builder -> builder.field("test_keyword", "0")); doWithQuery(SELECT_WILDCARD, results -> { - results.next(); + results.next(); // docId: 1 assertTrue(results.getBoolean("test_boolean")); - for (String fld : fieldsNames) { + for (String fld : FIELDS_NAMES) { assertTrue("Expected: but was: for field " + fld, results.getBoolean(fld)); assertEquals("Expected: but was: for field " + fld, true, results.getObject(fld, Boolean.class)); } @@ -1106,9 +1274,9 @@ public void testGettingBooleanValues() throws Exception { sqle = expectThrows(SQLException.class, () -> results.getBoolean("test_date_nanos")); assertErrorMessageForDateTimeValues(sqle, Boolean.class, toMilliSeconds(randomDateNanos1), extractNanosOnly(randomDateNanos1)); - results.next(); + results.next(); // docId: 2 assertFalse(results.getBoolean("test_boolean")); - for (String fld : fieldsNames) { + for (String fld : FIELDS_NAMES) { assertFalse("Expected: but was: for field " + fld, results.getBoolean(fld)); assertEquals("Expected: but was: for field " + fld, false, results.getObject(fld, Boolean.class)); } @@ -1124,18 +1292,43 @@ public void testGettingBooleanValues() throws Exception { sqle = expectThrows(SQLException.class, () -> results.getObject("test_date_nanos", Boolean.class)); assertErrorMessageForDateTimeValues(sqle, Boolean.class, toMilliSeconds(randomDateNanos2), extractNanosOnly(randomDateNanos2)); - results.next(); - for (String fld : fieldsNames.stream().filter(f -> f.equals("test_keyword") == false).collect(Collectors.toSet())) { + results.next(); // docId: 3 + for (String fld : FIELDS_NAMES.stream().filter(f -> f.equals("test_keyword") == false).collect(Collectors.toSet())) { assertTrue("Expected: but was: for field " + fld, results.getBoolean(fld)); assertEquals("Expected: but was: for field " + fld, true, results.getObject(fld, Boolean.class)); } - results.next(); + results.next(); // docId: 4 assertFalse(results.getBoolean("test_keyword")); assertEquals(false, results.getObject("test_keyword", Boolean.class)); }); } + public void testGettingBooleanValuesFromUnsignedLong() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + createIndex("test"); + updateMappingForNumericValuesTests("test", singletonList(UNSIGNED_LONG_FIELD)); + + indexTestFieldsDoc("1", BigInteger.ONE); + indexTestFieldsDoc("2", BigInteger.ZERO); + indexTestFieldsDoc("3", randomValueOtherThan(BigInteger.ZERO, ESTestCase::randomBigInteger)); + + doWithQuery(SELECT_WILDCARD, results -> { + results.next(); // docId: 1 + assertTrue(results.getBoolean(UNSIGNED_LONG_FIELD)); + assertTrue(results.getObject(UNSIGNED_LONG_FIELD, Boolean.class)); + + results.next(); // docId: 2 + assertFalse(results.getBoolean(UNSIGNED_LONG_FIELD)); + assertFalse(results.getObject(UNSIGNED_LONG_FIELD, Boolean.class)); + + results.next(); // docId: 3 + assertTrue(results.getBoolean(UNSIGNED_LONG_FIELD)); + assertTrue(results.getObject(UNSIGNED_LONG_FIELD, Boolean.class)); + }); + } + private void setupDataForDateTimeTests(long randomLongDate) throws IOException { setupDataForDateTimeTests(randomLongDate, null); } @@ -1149,7 +1342,7 @@ private void setupDataForDateTimeTests(long randomLongDate, Long randomLongDateN builder.startObject("test_date_nanos").field("type", "date_nanos").endObject(); }); - indexSimpleDocumentWithTrueValues(randomLongDate, randomLongDateNanos); + indexSimpleDocumentWithBooleanValues("1", true, randomLongDate, randomLongDateNanos); index("test", "2", builder -> { builder.timeField("test_date", null); builder.timeField("test_date_nanos", null); @@ -1165,9 +1358,9 @@ public void testGettingDateWithoutCalendar() throws Exception { Date expectedDate = asDate(randomLongDate, getZoneFromOffset(randomLongDate)); assertEquals(expectedDate, results.getDate("test_date")); - assertEquals(expectedDate, results.getDate(9)); + assertEquals(expectedDate, results.getDate(FIELDS_NAMES.size() + 2)); assertEquals(expectedDate, results.getObject("test_date", Date.class)); - assertEquals(expectedDate, results.getObject(9, Date.class)); + assertEquals(expectedDate, results.getObject(FIELDS_NAMES.size() + 2, Date.class)); // bulk validation for all fields which are not of type date validateErrorsForDateTimeTestsWithoutCalendar(results::getDate, "Date"); @@ -1224,7 +1417,7 @@ public void testGettingDateWithCalendar() throws Exception { Date expectedDate = new Date(c.getTimeInMillis()); assertEquals(expectedDate, results.getDate("test_date", c)); - assertEquals(expectedDate, results.getDate(9, c)); + assertEquals(expectedDate, results.getDate(FIELDS_NAMES.size() + 2, c)); // bulk validation for all fields which are not of type date validateErrorsForDateTimeTestsWithCalendar(c, results::getDate); @@ -1278,9 +1471,9 @@ public void testGettingTimeWithoutCalendar() throws Exception { Time expectedTime = asTime(randomLongDate, getZoneFromOffset(randomLongDate)); assertEquals(expectedTime, results.getTime("test_date")); - assertEquals(expectedTime, results.getTime(9)); + assertEquals(expectedTime, results.getTime(FIELDS_NAMES.size() + 2)); assertEquals(expectedTime, results.getObject("test_date", Time.class)); - assertEquals(expectedTime, results.getObject(9, Time.class)); + assertEquals(expectedTime, results.getObject(FIELDS_NAMES.size() + 2, Time.class)); validateErrorsForDateTimeTestsWithoutCalendar(results::getTime, "Time"); @@ -1335,7 +1528,7 @@ public void testGettingTimeWithCalendar() throws Exception { Time expectedTime = new Time(c.getTimeInMillis()); assertEquals(expectedTime, results.getTime("test_date", c)); - assertEquals(expectedTime, results.getTime(9, c)); + assertEquals(expectedTime, results.getTime(FIELDS_NAMES.size() + 2, c)); validateErrorsForDateTimeTestsWithCalendar(c, results::getTime); @@ -1455,7 +1648,7 @@ public void testGettingTimestampWithCalendar() throws IOException, SQLException c.setTimeInMillis(randomLongDate); assertEquals(new Timestamp(c.getTimeInMillis()), results.getTimestamp("test_date", c)); - assertEquals(new Timestamp(c.getTimeInMillis()), results.getTimestamp(9, c)); + assertEquals(new Timestamp(c.getTimeInMillis()), results.getTimestamp(FIELDS_NAMES.size() + 2, c)); results.next(); assertNull(results.getTimestamp("test_date")); @@ -1491,6 +1684,32 @@ public void testGettingTimestampWithCalendar_DateNanos() throws IOException, SQL }); } + public void testErrorsValidationForDateTimeTypesConvertingToUnsignedLong() throws IOException, SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + createIndex("test"); + updateMappingForNumericValuesTests("test", singletonList(UNSIGNED_LONG_FIELD)); + + indexTestFieldsDoc("1", BigInteger.ONE); + + doWithQuery(SELECT_WILDCARD, results -> { + results.next(); + + SQLException sqle = expectThrows(SQLException.class, () -> results.getDate(UNSIGNED_LONG_FIELD)); + assertEquals( + format(Locale.ROOT, "Unable to convert value [%.127s] of type [%s] to a Date", BigInteger.ONE, UNSIGNED_LONG_TYPE_NAME), + sqle.getMessage() + ); + + sqle = expectThrows(SQLException.class, () -> results.getTime(UNSIGNED_LONG_FIELD)); + assertEquals( + format(Locale.ROOT, "Unable to convert value [%.127s] of type [%s] to a Time", BigInteger.ONE, UNSIGNED_LONG_TYPE_NAME), + sqle.getMessage() + ); + }); + + } + public void testScalarOnDates() throws IOException, SQLException { createIndex("test"); updateMapping("test", builder -> builder.startObject("test_date").field("type", "date").endObject()); @@ -1815,6 +2034,18 @@ public void testGettingNullValues() throws SQLException { }); } + public void testGettingNullUnsignedLong() throws SQLException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support UNSIGNED_LONGs", isUnsignedLongSupported()); + + String query = "SELECT CAST(NULL AS UNSIGNED_LONG) as ul"; + doWithQuery(query, results -> { + results.next(); + + assertNull(results.getObject("ul")); + assertEquals(0.0f, results.getFloat("ul"), 0f); + }); + } + /* * Checks StackOverflowError fix for https://github.com/elastic/elasticsearch/pull/31735 */ @@ -1845,7 +2076,7 @@ public void testUnsupportedGetMethods() throws IOException, SQLException { index("test", "1", builder -> builder.field("test", "test")); try ( Connection conn = esJdbc(); - PreparedStatement statement = conn.prepareStatement("SELECT * FROM test"); + PreparedStatement statement = conn.prepareStatement(SELECT_WILDCARD); ResultSet r = statement.executeQuery() ) { @@ -1883,7 +2114,7 @@ public void testUnsupportedUpdateMethods() throws IOException, SQLException { index("test", "1", builder -> builder.field("test", "test")); try ( Connection conn = esJdbc(); - PreparedStatement statement = conn.prepareStatement("SELECT * FROM test"); + PreparedStatement statement = conn.prepareStatement(SELECT_WILDCARD); ResultSet r = statement.executeQuery() ) { r.next(); @@ -2157,22 +2388,23 @@ private List createTestDataForNumericValueTests(Supplier clazz = random1.getClass(); - String primitiveName = clazz.getSimpleName().toLowerCase(Locale.ROOT); + EsType esType = of(random1.getClass()); + assertNotNull(esType); + String esTypeName = esType.getName().toLowerCase(Locale.ROOT); createIndex("test"); updateMapping("test", builder -> { - builder.startObject("test_" + primitiveName).field("type", primitiveName).endObject(); - builder.startObject("test_null_" + primitiveName).field("type", primitiveName).endObject(); + builder.startObject("test_" + esTypeName).field("type", esTypeName).endObject(); + builder.startObject("test_null_" + esTypeName).field("type", esTypeName).endObject(); builder.startObject("test_keyword").field("type", "keyword").endObject(); }); index("test", "1", builder -> { - builder.field("test_" + primitiveName, random1); - builder.field("test_null_" + primitiveName, (Byte) null); + builder.field("test_" + esTypeName, random1); + builder.field("test_null_" + esTypeName, (Byte) null); }); index("test", "2", builder -> { - builder.field("test_" + primitiveName, random2); + builder.field("test_" + esTypeName, random2); builder.field("test_keyword", random3); }); @@ -2199,16 +2431,17 @@ private void createTestDataForBooleanValueTests() throws IOException { }); } - private void indexSimpleDocumentWithTrueValues(long randomLongDate, Long randomLongNanos) throws IOException { - index("test", "1", builder -> { - builder.field("test_boolean", true); - builder.field("test_byte", 1); - builder.field("test_integer", 1); - builder.field("test_long", 1L); - builder.field("test_short", 1); - builder.field("test_double", 1d); - builder.field("test_float", 1f); - builder.field("test_keyword", "true"); + private void indexSimpleDocumentWithBooleanValues(String docId, boolean bool, Long randomLongDate, Long randomLongNanos) + throws IOException { + index("test", docId, builder -> { + builder.field("test_boolean", bool); + builder.field("test_byte", bool ? 1 : 0); + builder.field("test_integer", bool ? 1 : 0); + builder.field("test_long", bool ? 1L : 0L); + builder.field("test_short", bool ? 1 : 0); + builder.field("test_double", bool ? 1d : 0d); + builder.field("test_float", bool ? 1f : 0f); + builder.field("test_keyword", bool ? "true" : "false"); builder.field("test_date", randomLongDate); if (randomLongNanos != null) { builder.field("test_date_nanos", asTimestampWithNanos(randomLongNanos)); @@ -2216,6 +2449,27 @@ private void indexSimpleDocumentWithTrueValues(long randomLongDate, Long randomL }); } + private static void indexTestFieldsDoc(String docId, Object... values) throws IOException { + index("test", docId, builder -> { + for (Object value : values) { + String classSimpleName = value.getClass().getSimpleName().toLowerCase(Locale.ROOT); + switch (classSimpleName) { + case "biginteger": + builder.field(UNSIGNED_LONG_FIELD, value); + break; + case "date": + builder.field("test_" + classSimpleName, ((Date) value).getTime()); + break; + case "string": + builder.field("test_keyword", value); + break; + default: + builder.field("test_" + classSimpleName, value); + } + } + }); + } + /** * Creates test data for all numeric get* methods. All values random and different from the other numeric fields already generated. * It returns a map containing the field name and its randomly generated value to be later used in checking the returned values. @@ -2259,14 +2513,18 @@ private Map createTestDataForNumericValueTypes(Supplier return map; } - private static void updateMappingForNumericValuesTests(String indexName) throws IOException { + private static void updateMappingForNumericValuesTests(String indexName, List fieldsNames) throws IOException { updateMapping(indexName, builder -> { for (String field : fieldsNames) { - builder.startObject(field).field("type", field.substring(5)).endObject(); + builder.startObject(field).field("type", field.substring("test_".length())).endObject(); } }); } + private static void updateMappingForNumericValuesTests(String indexName) throws IOException { + updateMappingForNumericValuesTests(indexName, FIELDS_NAMES); + } + private void assertThrowsUnsupportedAndExpectErrorMessage(ThrowingRunnable runnable, String message) { SQLException sqle = expectThrows(SQLFeatureNotSupportedException.class, runnable); assertEquals(message, sqle.getMessage()); diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/FieldExtractorTestCase.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/FieldExtractorTestCase.java index 4dc08d6237972..9bd70ae6b51fa 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/FieldExtractorTestCase.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/FieldExtractorTestCase.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InputStream; +import java.math.BigInteger; import java.sql.JDBCType; import java.util.ArrayList; import java.util.Collections; @@ -187,6 +188,7 @@ public void testWildcardField() throws IOException { * "long/integer/short/byte_field": { * "type": "long/integer/short/byte" * } + * Note: no unsigned_long tested -- the mapper for it won't accept float formats. */ public void testFractionsForNonFloatingPointTypes() throws IOException { String floatingPointNumber = "123.456"; @@ -281,6 +283,17 @@ public void testByteFieldType() throws IOException { testField("byte", ((Number) randomByte()).intValue()); } + /* + * "unsigned_long_field": { + * "type": "unsigned_long", + * "ignore_malformed": true/false + * } + */ + public void testUnsignedLongFieldType() throws IOException { + // randomBigInteger() can produce a value that fits into a Long, which is what testField() will then recover + testField("unsigned_long", BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.valueOf(randomNonNegativeLong()))); + } + private void testField(String fieldType, Object value) throws IOException { String fieldName = fieldType + "_field"; String query = "SELECT " + fieldName + " FROM test"; @@ -1602,6 +1615,7 @@ private JDBCType jdbcTypeFor(String esType) { case "half_float" -> JDBCType.FLOAT; case "scaled_float" -> JDBCType.DOUBLE; case "ip" -> JDBCType.VARCHAR; + case "unsigned_long" -> JDBCType.BIGINT; default -> throw new AssertionError("Illegal value [" + esType + "] for data type"); }; } diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java index 43ec9b0a4a5b9..0811b4d9a448e 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java @@ -29,6 +29,7 @@ import java.util.Locale; import java.util.Map; +import static org.elasticsearch.xpack.ql.util.NumericUtils.UNSIGNED_LONG_MAX; import static org.elasticsearch.xpack.sql.proto.CoreProtocol.SQL_QUERY_REST_ENDPOINT; import static org.elasticsearch.xpack.sql.proto.Mode.CLI; import static org.elasticsearch.xpack.sql.proto.RequestInfo.CLIENT_IDS; @@ -54,6 +55,7 @@ public void testNumericTypes() throws IOException { assertQuery("SELECT -2123", "-2123", "integer", -2123, 11); assertQuery("SELECT 1234567890123", "1234567890123", "long", 1234567890123L, 20); assertQuery("SELECT -1234567890123", "-1234567890123", "long", -1234567890123L, 20); + assertQuery("SELECT 18446744073709551615", "18446744073709551615", "unsigned_long", UNSIGNED_LONG_MAX, 20); assertQuery("SELECT 1234567890123.34", "1234567890123.34", "double", 1234567890123.34, 25); assertQuery("SELECT -1234567890123.34", "-1234567890123.34", "double", -1234567890123.34, 25); assertQuery("SELECT CAST(1234.34 AS REAL)", "CAST(1234.34 AS REAL)", "float", 1234.34f, 15); diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/CsvTestUtils.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/CsvTestUtils.java index 42956c0ae8bc8..36114694b9e9b 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/CsvTestUtils.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/CsvTestUtils.java @@ -153,6 +153,7 @@ private static String resolveColumnType(String type) { case "ts" -> "timestamp"; case "bt" -> "byte"; case "sh" -> "short"; + case "ul" -> "bigdecimal"; // CSV JDBC driver lacks biginteger support default -> type; }; } diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java index 8acbfe809e592..eac026689f72f 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/DataLoader.java @@ -67,6 +67,7 @@ protected static void loadEmpDatasetIntoEs(RestClient client) throws Exception { loadEmpDatasetWithExtraIntoEs(client, "test_emp_copy", "employees"); loadLogsDatasetIntoEs(client, "logs", "logs"); loadLogNanosDatasetIntoEs(client, "logs_nanos", "logs_nanos"); + loadLogUnsignedLongIntoEs(client, "logs_unsigned_long", "logs_unsigned_long"); makeAlias(client, "test_alias", "test_emp", "test_emp_copy"); makeAlias(client, "test_alias_emp", "test_emp", "test_emp_copy"); // frozen index @@ -151,6 +152,9 @@ private static void loadEmpDatasetIntoEs(RestClient client, String index, String createIndex.startObject("birth_date").field("type", "date").endObject(); createIndex.startObject("hire_date").field("type", "date").endObject(); createIndex.startObject("salary").field("type", "integer").endObject(); + if (extraFields) { + createIndex.startObject("salary_ul").field("type", "unsigned_long").endObject(); + } createIndex.startObject("languages").field("type", "byte").endObject(); { createIndex.startObject("dep").field("type", "nested"); @@ -226,8 +230,12 @@ private static void loadEmpDatasetIntoEs(RestClient client, String index, String } hadLastItem = true; bulk.append('"').append(titles.get(f)).append("\":\"").append(fields.get(f)).append('"'); - if (titles.get(f).equals("gender") && extraFields) { - bulk.append(",\"extra_gender\":\"Female\""); + if (extraFields) { + if (titles.get(f).equals("gender")) { + bulk.append(",\"extra_gender\":\"Female\""); + } else if (titles.get(f).equals("salary")) { + bulk.append(",\"salary_ul\":" + fields.get(f)); + } } } if ((titles.get(f).equals("first_name") || titles.get(f).equals("last_name")) && extraFields && setWildcardName) { @@ -267,14 +275,7 @@ private static void loadEmpDatasetIntoEs(RestClient client, String index, String } protected static void loadLogsDatasetIntoEs(RestClient client, String index, String filename) throws Exception { - Request request = new Request("PUT", "/" + index); XContentBuilder createIndex = JsonXContent.contentBuilder().startObject(); - createIndex.startObject("settings"); - { - createIndex.field("number_of_shards", 1); - createIndex.field("number_of_replicas", 1); - } - createIndex.endObject(); createIndex.startObject("mappings"); { createIndex.startObject("properties"); @@ -290,82 +291,49 @@ protected static void loadLogsDatasetIntoEs(RestClient client, String index, Str } createIndex.endObject(); } - createIndex.endObject().endObject(); - request.setJsonEntity(Strings.toString(createIndex)); - client.performRequest(request); + createIndex.endObject(); - request = new Request("POST", "/" + index + "/_bulk?refresh=wait_for"); - request.addParameter("refresh", "true"); - StringBuilder bulk = new StringBuilder(); - csvToLines(filename, (titles, fields) -> { - bulk.append("{\"index\":{\"_id\":\"" + fields.get(0) + "\"}}\n"); - bulk.append("{"); - for (int f = 0; f < titles.size(); f++) { - if (Strings.hasText(fields.get(f))) { - if (f > 0) { - bulk.append(","); - } - bulk.append('"').append(titles.get(f)).append("\":\"").append(fields.get(f)).append('"'); - } - } - bulk.append("}\n"); - }); - request.setJsonEntity(bulk.toString()); - client.performRequest(request); + loadDatasetIntoEs(client, index, filename, createIndex); } protected static void loadLogNanosDatasetIntoEs(RestClient client, String index, String filename) throws Exception { - Request request = new Request("PUT", "/" + index); XContentBuilder createIndex = JsonXContent.contentBuilder().startObject(); - createIndex.startObject("settings"); + createIndex.startObject("mappings"); { - createIndex.field("number_of_shards", 1); - createIndex.field("number_of_replicas", 1); + createIndex.startObject("properties"); + { + createIndex.startObject("id").field("type", "integer").endObject(); + createIndex.startObject("@timestamp").field("type", "date_nanos").endObject(); + createIndex.startObject("status").field("type", "keyword").endObject(); + } + createIndex.endObject(); } createIndex.endObject(); + + loadDatasetIntoEs(client, index, filename, createIndex); + } + + protected static void loadLogUnsignedLongIntoEs(RestClient client, String index, String filename) throws Exception { + XContentBuilder createIndex = JsonXContent.contentBuilder().startObject(); createIndex.startObject("mappings"); { createIndex.startObject("properties"); { createIndex.startObject("id").field("type", "integer").endObject(); createIndex.startObject("@timestamp").field("type", "date_nanos").endObject(); + createIndex.startObject("bytes_in").field("type", "unsigned_long").endObject(); + createIndex.startObject("bytes_out").field("type", "unsigned_long").endObject(); createIndex.startObject("status").field("type", "keyword").endObject(); } createIndex.endObject(); } - createIndex.endObject().endObject(); - request.setJsonEntity(Strings.toString(createIndex)); - client.performRequest(request); + createIndex.endObject(); - request = new Request("POST", "/" + index + "/_bulk?refresh=wait_for"); - request.addParameter("refresh", "true"); - StringBuilder bulk = new StringBuilder(); - csvToLines(filename, (titles, fields) -> { - bulk.append("{\"index\":{\"_id\":\"" + fields.get(0) + "\"}}\n"); - bulk.append("{"); - for (int f = 0; f < titles.size(); f++) { - if (Strings.hasText(fields.get(f))) { - if (f > 0) { - bulk.append(","); - } - bulk.append('"').append(titles.get(f)).append("\":\"").append(fields.get(f)).append('"'); - } - } - bulk.append("}\n"); - }); - request.setJsonEntity(bulk.toString()); - client.performRequest(request); + loadDatasetIntoEs(client, index, filename, createIndex); } protected static void loadLibDatasetIntoEs(RestClient client, String index) throws Exception { - Request request = new Request("PUT", "/" + index); XContentBuilder createIndex = JsonXContent.contentBuilder().startObject(); - createIndex.startObject("settings"); - { - createIndex.field("number_of_shards", 1); - createIndex.field("number_of_replicas", 1); - } - createIndex.endObject(); createIndex.startObject("mappings"); { createIndex.startObject("properties"); @@ -377,21 +345,39 @@ protected static void loadLibDatasetIntoEs(RestClient client, String index) thro } createIndex.endObject(); } - createIndex.endObject().endObject(); + createIndex.endObject(); + + loadDatasetIntoEs(client, index, "library", createIndex); + } + + // createIndex must be startObject()'d, but not endObject()'d: the index settings are added at the end + protected static void loadDatasetIntoEs(RestClient client, String index, String filename, XContentBuilder createIndex) + throws Exception { + createIndex.startObject("settings"); + { + createIndex.field("number_of_shards", 1); + createIndex.field("number_of_replicas", 1); + } + createIndex.endObject(); + createIndex.endObject(); + + Request request = new Request("PUT", "/" + index); request.setJsonEntity(Strings.toString(createIndex)); client.performRequest(request); request = new Request("POST", "/" + index + "/_bulk?refresh=wait_for"); request.addParameter("refresh", "true"); StringBuilder bulk = new StringBuilder(); - csvToLines("library", (titles, fields) -> { + csvToLines(filename, (titles, fields) -> { bulk.append("{\"index\":{\"_id\":\"" + fields.get(0) + "\"}}\n"); bulk.append("{"); for (int f = 0; f < titles.size(); f++) { - if (f > 0) { - bulk.append(","); + if (fields.size() > f && Strings.hasText(fields.get(f))) { + if (f > 0) { + bulk.append(","); + } + bulk.append('"').append(titles.get(f)).append("\":\"").append(fields.get(f)).append('"'); } - bulk.append('"').append(titles.get(f)).append("\":\"").append(fields.get(f)).append('"'); } bulk.append("}\n"); }); diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcAssert.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcAssert.java index dedce0af237e9..77e6b3ac57a55 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcAssert.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcAssert.java @@ -17,6 +17,8 @@ import org.relique.jdbc.csv.CsvResultSet; import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; import java.sql.Date; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -160,6 +162,7 @@ public static void assertResultSetMetaData(ResultSet expected, ResultSet actual, // use the type not the name (timestamp with timezone returns spaces for example) int expectedType = typeOf(expectedMeta.getColumnType(column), lenientDataType); int actualType = typeOf(actualMeta.getColumnType(column), lenientDataType); + String actualTypeName = actualMeta.getColumnTypeName(column); // since H2 cannot use a fixed timezone, the data is stored in UTC (and thus with timezone) if (expectedType == Types.TIMESTAMP_WITH_TIMEZONE) { @@ -185,6 +188,11 @@ public static void assertResultSetMetaData(ResultSet expected, ResultSet actual, expectedType = Types.NULL; } + // csv and h2 both map values larger than Long.MAX_VALUE to Decimal types + if (expectedType == Types.DECIMAL && actualTypeName.compareTo(EsType.UNSIGNED_LONG.getName()) == 0) { + expectedType = EsType.UNSIGNED_LONG.getVendorTypeNumber(); + } + // when lenient is used, an int is equivalent to a short, etc... assertEquals( "Different column type for column [" + expectedName + "] (" + nameOf(expectedType) + " != " + nameOf(actualType) + ")", @@ -252,6 +260,7 @@ private static void doAssertResultSetData( case "Time" -> "java.sql.Time"; case "Timestamp" -> "java.sql.Timestamp"; case "Int" -> "java.lang.Integer"; + case "BigDecimal" -> "java.math.BigDecimal"; default -> "java.lang." + columnClassName; }; } @@ -319,6 +328,10 @@ else if (type == Types.DOUBLE) { else if (type == Types.VARCHAR && actualObject instanceof TemporalAmount) { assertEquals(msg, expectedObject, StringUtils.toString(actualObject)); } + // unsigned_long + else if (expectedObject instanceof BigDecimal && actualObject instanceof BigInteger) { + assertEquals(expectedObject, new BigDecimal((BigInteger) actualObject)); + } // finally the actual comparison else { assertEquals(msg, expectedObject, actualObject); diff --git a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java index 42c95ff746c6b..c5121168116e2 100644 --- a/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java +++ b/x-pack/plugin/sql/qa/server/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java @@ -11,6 +11,7 @@ import org.apache.http.HttpEntity; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; +import org.elasticsearch.Version; import org.elasticsearch.client.Request; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; @@ -22,6 +23,7 @@ import org.elasticsearch.core.Nullable; import org.elasticsearch.core.Tuple; import org.elasticsearch.test.NotEqualMessageBuilder; +import org.elasticsearch.test.VersionUtils; import org.elasticsearch.xcontent.XContentBuilder; import org.elasticsearch.xcontent.json.JsonXContent; import org.elasticsearch.xpack.sql.proto.CoreProtocol; @@ -55,6 +57,7 @@ import static java.util.Collections.unmodifiableMap; import static org.elasticsearch.common.Strings.hasText; import static org.elasticsearch.xpack.ql.TestUtils.getNumberOfSearchContexts; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.INTRODUCING_UNSIGNED_LONG; import static org.elasticsearch.xpack.sql.proto.CoreProtocol.COLUMNS_NAME; import static org.elasticsearch.xpack.sql.proto.CoreProtocol.HEADER_NAME_ASYNC_ID; import static org.elasticsearch.xpack.sql.proto.CoreProtocol.HEADER_NAME_ASYNC_PARTIAL; @@ -1152,6 +1155,16 @@ public void testBinaryFieldFiltering() throws IOException { deleteIndex("test_binary"); } + public void testPreventedUnsignedLongMaskedAccess() throws IOException { + loadUnsignedLongTestData(); + Version version = VersionUtils.randomVersionBetween(random(), null, VersionUtils.getPreviousVersion(INTRODUCING_UNSIGNED_LONG)); + String query = query("SELECT unsigned_long::STRING FROM " + indexPattern("test")).version(version.toString()).toString(); + expectBadRequest( + () -> runSql(new StringEntity(query, ContentType.APPLICATION_JSON), "", randomMode()), + containsString("Cannot use field [unsigned_long] with unsupported type [UNSIGNED_LONG]") + ); + } + private void executeQueryWithNextPage(String format, String expectedHeader, String expectedLineFormat) throws IOException { int size = 20; String[] docs = new String[size]; @@ -1222,6 +1235,22 @@ private static void bulkLoadTestData(int count) throws IOException { provisioningClient().performRequest(request); } + private void loadUnsignedLongTestData() throws IOException { + Request request = new Request("PUT", "/test"); + request.setJsonEntity(""" + { + "mappings": { + "properties": { + "unsigned_long": { + "type": "unsigned_long" + } + } + } + }"""); + provisioningClient().performRequest(request); + index("{\"unsigned_long\": 18446744073709551615}"); + } + protected static Tuple runSqlAsText(String sql, String accept) throws IOException { return runSqlAsText(StringUtils.EMPTY, new StringEntity(query(sql).toString(), ContentType.APPLICATION_JSON), accept); } diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/alias.csv-spec b/x-pack/plugin/sql/qa/server/src/main/resources/alias.csv-spec index e5c56644fb925..95d223bb33a93 100644 --- a/x-pack/plugin/sql/qa/server/src/main/resources/alias.csv-spec +++ b/x-pack/plugin/sql/qa/server/src/main/resources/alias.csv-spec @@ -28,63 +28,65 @@ emp_no:i | first_name:s describeAlias DESCRIBE test_alias; - column | type | mapping -----------------------+---------------+--------------- -birth_date |TIMESTAMP |datetime -dep |STRUCT |nested -dep.dep_id |VARCHAR |keyword -dep.dep_name |VARCHAR |text -dep.dep_name.keyword |VARCHAR |keyword -dep.from_date |TIMESTAMP |datetime -dep.to_date |TIMESTAMP |datetime -emp_no |INTEGER |integer -extra |STRUCT |object -extra.info |STRUCT |object -extra.info.gender |VARCHAR |keyword -extra_gender |VARCHAR |keyword -extra_no |INTEGER |integer -first_name |VARCHAR |text -first_name.keyword |VARCHAR |keyword -gender |VARCHAR |keyword -hire_date |TIMESTAMP |datetime -languages |TINYINT |byte -last_name |VARCHAR |text -last_name.keyword |VARCHAR |keyword -name |VARCHAR |keyword -null_constant |VARCHAR |keyword -salary |INTEGER |integer -wildcard_name |VARCHAR |keyword + column | type | mapping +--------------------+---------------+--------------- +birth_date |TIMESTAMP |datetime +dep |STRUCT |nested +dep.dep_id |VARCHAR |keyword +dep.dep_name |VARCHAR |text +dep.dep_name.keyword|VARCHAR |keyword +dep.from_date |TIMESTAMP |datetime +dep.to_date |TIMESTAMP |datetime +emp_no |INTEGER |integer +extra |STRUCT |object +extra.info |STRUCT |object +extra.info.gender |VARCHAR |keyword +extra_gender |VARCHAR |keyword +extra_no |INTEGER |integer +first_name |VARCHAR |text +first_name.keyword |VARCHAR |keyword +gender |VARCHAR |keyword +hire_date |TIMESTAMP |datetime +languages |TINYINT |byte +last_name |VARCHAR |text +last_name.keyword |VARCHAR |keyword +name |VARCHAR |keyword +null_constant |VARCHAR |keyword +salary |INTEGER |integer +salary_ul |NUMERIC |unsigned_long +wildcard_name |VARCHAR |keyword ; describePattern DESCRIBE "test_*"; - column | type | mapping -----------------------+---------------+--------------- -birth_date |TIMESTAMP |datetime -dep |STRUCT |nested -dep.dep_id |VARCHAR |keyword -dep.dep_name |VARCHAR |text -dep.dep_name.keyword |VARCHAR |keyword -dep.from_date |TIMESTAMP |datetime -dep.to_date |TIMESTAMP |datetime -emp_no |INTEGER |integer -extra |STRUCT |object -extra.info |STRUCT |object -extra.info.gender |VARCHAR |keyword -extra_gender |VARCHAR |keyword -extra_no |INTEGER |integer -first_name |VARCHAR |text -first_name.keyword |VARCHAR |keyword -gender |VARCHAR |keyword -hire_date |TIMESTAMP |datetime -languages |TINYINT |byte -last_name |VARCHAR |text -last_name.keyword |VARCHAR |keyword -name |VARCHAR |keyword -null_constant |VARCHAR |keyword -salary |INTEGER |integer -wildcard_name |VARCHAR |keyword + column | type | mapping +--------------------+---------------+--------------- +birth_date |TIMESTAMP |datetime +dep |STRUCT |nested +dep.dep_id |VARCHAR |keyword +dep.dep_name |VARCHAR |text +dep.dep_name.keyword|VARCHAR |keyword +dep.from_date |TIMESTAMP |datetime +dep.to_date |TIMESTAMP |datetime +emp_no |INTEGER |integer +extra |STRUCT |object +extra.info |STRUCT |object +extra.info.gender |VARCHAR |keyword +extra_gender |VARCHAR |keyword +extra_no |INTEGER |integer +first_name |VARCHAR |text +first_name.keyword |VARCHAR |keyword +gender |VARCHAR |keyword +hire_date |TIMESTAMP |datetime +languages |TINYINT |byte +last_name |VARCHAR |text +last_name.keyword |VARCHAR |keyword +name |VARCHAR |keyword +null_constant |VARCHAR |keyword +salary |INTEGER |integer +salary_ul |NUMERIC |unsigned_long +wildcard_name |VARCHAR |keyword ; showAlias diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/command.csv-spec b/x-pack/plugin/sql/qa/server/src/main/resources/command.csv-spec index a06dc0e7cb53e..c3f3af296adb3 100644 --- a/x-pack/plugin/sql/qa/server/src/main/resources/command.csv-spec +++ b/x-pack/plugin/sql/qa/server/src/main/resources/command.csv-spec @@ -9,15 +9,15 @@ showFunctions SHOW FUNCTIONS; name:s | type:s -AVG |AGGREGATE +AVG |AGGREGATE COUNT |AGGREGATE FIRST |AGGREGATE FIRST_VALUE |AGGREGATE LAST |AGGREGATE LAST_VALUE |AGGREGATE MAX |AGGREGATE -MIN |AGGREGATE -SUM |AGGREGATE +MIN |AGGREGATE +SUM |AGGREGATE KURTOSIS |AGGREGATE MAD |AGGREGATE PERCENTILE |AGGREGATE @@ -64,11 +64,11 @@ DAY_OF_MONTH |SCALAR DAY_OF_WEEK |SCALAR DAY_OF_YEAR |SCALAR DOM |SCALAR -DOW |SCALAR +DOW |SCALAR DOY |SCALAR FORMAT |SCALAR HOUR |SCALAR -HOUR_OF_DAY |SCALAR +HOUR_OF_DAY |SCALAR IDOW |SCALAR ISODAYOFWEEK |SCALAR ISODOW |SCALAR @@ -78,16 +78,16 @@ ISO_DAY_OF_WEEK |SCALAR ISO_WEEK_OF_YEAR |SCALAR IW |SCALAR IWOY |SCALAR -MINUTE |SCALAR -MINUTE_OF_DAY |SCALAR -MINUTE_OF_HOUR |SCALAR -MONTH |SCALAR -MONTHNAME |SCALAR -MONTH_NAME |SCALAR -MONTH_OF_YEAR |SCALAR +MINUTE |SCALAR +MINUTE_OF_DAY |SCALAR +MINUTE_OF_HOUR |SCALAR +MONTH |SCALAR +MONTHNAME |SCALAR +MONTH_NAME |SCALAR +MONTH_OF_YEAR |SCALAR NOW |SCALAR -QUARTER |SCALAR -SECOND |SCALAR +QUARTER |SCALAR +SECOND |SCALAR SECOND_OF_MINUTE |SCALAR TIMESTAMPADD |SCALAR TIMESTAMPDIFF |SCALAR @@ -97,60 +97,60 @@ TIME_PARSE |SCALAR TODAY |SCALAR TO_CHAR |SCALAR WEEK |SCALAR -WEEK_OF_YEAR |SCALAR -YEAR |SCALAR -ABS |SCALAR -ACOS |SCALAR -ASIN |SCALAR -ATAN |SCALAR -ATAN2 |SCALAR -CBRT |SCALAR -CEIL |SCALAR -CEILING |SCALAR -COS |SCALAR -COSH |SCALAR -COT |SCALAR -DEGREES |SCALAR -E |SCALAR -EXP |SCALAR -EXPM1 |SCALAR -FLOOR |SCALAR -LOG |SCALAR -LOG10 |SCALAR -MOD |SCALAR -PI |SCALAR -POWER |SCALAR -RADIANS |SCALAR -RAND |SCALAR -RANDOM |SCALAR -ROUND |SCALAR -SIGN |SCALAR -SIGNUM |SCALAR -SIN |SCALAR -SINH |SCALAR -SQRT |SCALAR -TAN |SCALAR +WEEK_OF_YEAR |SCALAR +YEAR |SCALAR +ABS |SCALAR +ACOS |SCALAR +ASIN |SCALAR +ATAN |SCALAR +ATAN2 |SCALAR +CBRT |SCALAR +CEIL |SCALAR +CEILING |SCALAR +COS |SCALAR +COSH |SCALAR +COT |SCALAR +DEGREES |SCALAR +E |SCALAR +EXP |SCALAR +EXPM1 |SCALAR +FLOOR |SCALAR +LOG |SCALAR +LOG10 |SCALAR +MOD |SCALAR +PI |SCALAR +POWER |SCALAR +RADIANS |SCALAR +RAND |SCALAR +RANDOM |SCALAR +ROUND |SCALAR +SIGN |SCALAR +SIGNUM |SCALAR +SIN |SCALAR +SINH |SCALAR +SQRT |SCALAR +TAN |SCALAR TRUNC |SCALAR TRUNCATE |SCALAR ASCII |SCALAR -BIT_LENGTH |SCALAR -CHAR |SCALAR -CHARACTER_LENGTH |SCALAR -CHAR_LENGTH |SCALAR -CONCAT |SCALAR -INSERT |SCALAR -LCASE |SCALAR -LEFT |SCALAR -LENGTH |SCALAR -LOCATE |SCALAR -LTRIM |SCALAR -OCTET_LENGTH |SCALAR -POSITION |SCALAR -REPEAT |SCALAR -REPLACE |SCALAR -RIGHT |SCALAR -RTRIM |SCALAR -SPACE |SCALAR +BIT_LENGTH |SCALAR +CHAR |SCALAR +CHARACTER_LENGTH |SCALAR +CHAR_LENGTH |SCALAR +CONCAT |SCALAR +INSERT |SCALAR +LCASE |SCALAR +LEFT |SCALAR +LENGTH |SCALAR +LOCATE |SCALAR +LTRIM |SCALAR +OCTET_LENGTH |SCALAR +POSITION |SCALAR +REPEAT |SCALAR +REPLACE |SCALAR +RIGHT |SCALAR +RTRIM |SCALAR +SPACE |SCALAR STARTS_WITH |SCALAR SUBSTRING |SCALAR TRIM |SCALAR @@ -213,10 +213,10 @@ DAY_NAME |SCALAR DAY_OF_MONTH |SCALAR DAY_OF_WEEK |SCALAR DAY_OF_YEAR |SCALAR -HOUR_OF_DAY |SCALAR +HOUR_OF_DAY |SCALAR ISODAYOFWEEK |SCALAR ISO_DAY_OF_WEEK|SCALAR -MINUTE_OF_DAY |SCALAR +MINUTE_OF_DAY |SCALAR TODAY |SCALAR ; @@ -231,14 +231,15 @@ integTest |local showTables SHOW TABLES; -catalog | name | type | kind -integTest |empty_mapping |TABLE |INDEX -integTest |logs |TABLE |INDEX -integTest |logs_nanos |TABLE |INDEX -integTest |test_alias |VIEW |ALIAS -integTest |test_alias_emp |VIEW |ALIAS -integTest |test_emp |TABLE |INDEX -integTest |test_emp_copy |TABLE |INDEX +catalog | name | type | kind +integTest |empty_mapping |TABLE |INDEX +integTest |logs |TABLE |INDEX +integTest |logs_nanos |TABLE |INDEX +integTest |logs_unsigned_long |TABLE |INDEX +integTest |test_alias |VIEW |ALIAS +integTest |test_alias_emp |VIEW |ALIAS +integTest |test_emp |TABLE |INDEX +integTest |test_emp_copy |TABLE |INDEX ; showTablesSimpleLike @@ -281,14 +282,15 @@ integTest |test_alias_emp |VIEW |ALIAS showTablesLocalCatalog SHOW TABLES CATALOG 'integTest'; -catalog | name | type | kind -integTest |empty_mapping |TABLE |INDEX -integTest |logs |TABLE |INDEX -integTest |logs_nanos |TABLE |INDEX -integTest |test_alias |VIEW |ALIAS -integTest |test_alias_emp |VIEW |ALIAS -integTest |test_emp |TABLE |INDEX -integTest |test_emp_copy |TABLE |INDEX +catalog | name | type | kind +integTest |empty_mapping |TABLE |INDEX +integTest |logs |TABLE |INDEX +integTest |logs_nanos |TABLE |INDEX +integTest |logs_unsigned_long |TABLE |INDEX +integTest |test_alias |VIEW |ALIAS +integTest |test_alias_emp |VIEW |ALIAS +integTest |test_emp |TABLE |INDEX +integTest |test_emp_copy |TABLE |INDEX ; // DESCRIBE @@ -296,63 +298,65 @@ integTest |test_emp_copy |TABLE |INDEX describeSimpleLike DESCRIBE LIKE 'test_emp'; - column | type | mapping -----------------------+---------------+--------------- -birth_date |TIMESTAMP |datetime -dep |STRUCT |nested -dep.dep_id |VARCHAR |keyword -dep.dep_name |VARCHAR |text -dep.dep_name.keyword |VARCHAR |keyword -dep.from_date |TIMESTAMP |datetime -dep.to_date |TIMESTAMP |datetime -emp_no |INTEGER |integer -extra |STRUCT |object -extra.info |STRUCT |object -extra.info.gender |VARCHAR |keyword -extra_gender |VARCHAR |keyword -extra_no |INTEGER |integer -first_name |VARCHAR |text -first_name.keyword |VARCHAR |keyword -gender |VARCHAR |keyword -hire_date |TIMESTAMP |datetime -languages |TINYINT |byte -last_name |VARCHAR |text -last_name.keyword |VARCHAR |keyword -name |VARCHAR |keyword -null_constant |VARCHAR |keyword -salary |INTEGER |integer -wildcard_name |VARCHAR |keyword + column | type | mapping +--------------------+---------------+--------------- +birth_date |TIMESTAMP |datetime +dep |STRUCT |nested +dep.dep_id |VARCHAR |keyword +dep.dep_name |VARCHAR |text +dep.dep_name.keyword|VARCHAR |keyword +dep.from_date |TIMESTAMP |datetime +dep.to_date |TIMESTAMP |datetime +emp_no |INTEGER |integer +extra |STRUCT |object +extra.info |STRUCT |object +extra.info.gender |VARCHAR |keyword +extra_gender |VARCHAR |keyword +extra_no |INTEGER |integer +first_name |VARCHAR |text +first_name.keyword |VARCHAR |keyword +gender |VARCHAR |keyword +hire_date |TIMESTAMP |datetime +languages |TINYINT |byte +last_name |VARCHAR |text +last_name.keyword |VARCHAR |keyword +name |VARCHAR |keyword +null_constant |VARCHAR |keyword +salary |INTEGER |integer +salary_ul |NUMERIC |unsigned_long +wildcard_name |VARCHAR |keyword ; describeMultiLike DESCRIBE LIKE 'test_emp%'; - column | type | mapping -----------------------+---------------+--------------- -birth_date |TIMESTAMP |datetime -dep |STRUCT |nested -dep.dep_id |VARCHAR |keyword -dep.dep_name |VARCHAR |text -dep.dep_name.keyword |VARCHAR |keyword -dep.from_date |TIMESTAMP |datetime -dep.to_date |TIMESTAMP |datetime -emp_no |INTEGER |integer -extra |STRUCT |object -extra.info |STRUCT |object -extra.info.gender |VARCHAR |keyword -extra_gender |VARCHAR |keyword -extra_no |INTEGER |integer -first_name |VARCHAR |text -first_name.keyword |VARCHAR |keyword -gender |VARCHAR |keyword -hire_date |TIMESTAMP |datetime -languages |TINYINT |byte -last_name |VARCHAR |text -last_name.keyword |VARCHAR |keyword -name |VARCHAR |keyword -null_constant |VARCHAR |keyword -salary |INTEGER |integer -wildcard_name |VARCHAR |keyword + column | type | mapping +--------------------+---------------+--------------- +birth_date |TIMESTAMP |datetime +dep |STRUCT |nested +dep.dep_id |VARCHAR |keyword +dep.dep_name |VARCHAR |text +dep.dep_name.keyword|VARCHAR |keyword +dep.from_date |TIMESTAMP |datetime +dep.to_date |TIMESTAMP |datetime +emp_no |INTEGER |integer +extra |STRUCT |object +extra.info |STRUCT |object +extra.info.gender |VARCHAR |keyword +extra_gender |VARCHAR |keyword +extra_no |INTEGER |integer +first_name |VARCHAR |text +first_name.keyword |VARCHAR |keyword +gender |VARCHAR |keyword +hire_date |TIMESTAMP |datetime +languages |TINYINT |byte +last_name |VARCHAR |text +last_name.keyword |VARCHAR |keyword +name |VARCHAR |keyword +null_constant |VARCHAR |keyword +salary |INTEGER |integer +salary_ul |NUMERIC |unsigned_long +wildcard_name |VARCHAR |keyword ; describeSimpleIdentifier @@ -384,23 +388,23 @@ DESCRIBE CATALOG 'integTest' "test_emp"; column | type | mapping ----------------------+---------------+--------------- -birth_date |TIMESTAMP |datetime -dep |STRUCT |nested -dep.dep_id |VARCHAR |keyword -dep.dep_name |VARCHAR |text -dep.dep_name.keyword |VARCHAR |keyword -dep.from_date |TIMESTAMP |datetime -dep.to_date |TIMESTAMP |datetime -emp_no |INTEGER |integer -first_name |VARCHAR |text -first_name.keyword |VARCHAR |keyword -gender |VARCHAR |keyword -hire_date |TIMESTAMP |datetime -languages |TINYINT |byte -last_name |VARCHAR |text -last_name.keyword |VARCHAR |keyword -name |VARCHAR |keyword -salary |INTEGER |integer +birth_date |TIMESTAMP |datetime +dep |STRUCT |nested +dep.dep_id |VARCHAR |keyword +dep.dep_name |VARCHAR |text +dep.dep_name.keyword |VARCHAR |keyword +dep.from_date |TIMESTAMP |datetime +dep.to_date |TIMESTAMP |datetime +emp_no |INTEGER |integer +first_name |VARCHAR |text +first_name.keyword |VARCHAR |keyword +gender |VARCHAR |keyword +hire_date |TIMESTAMP |datetime +languages |TINYINT |byte +last_name |VARCHAR |text +last_name.keyword |VARCHAR |keyword +name |VARCHAR |keyword +salary |INTEGER |integer ; @@ -409,7 +413,7 @@ salary |INTEGER |integer describeIncludeExcludeIdentifier-Ignore DESCRIBE "test_*,-test_alias*"; - column | type | mapping + column | type | mapping --------------------+---------------+--------------- birth_date |TIMESTAMP |datetime dep |STRUCT |nested diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/logs_unsigned_long.csv b/x-pack/plugin/sql/qa/server/src/main/resources/logs_unsigned_long.csv new file mode 100644 index 0000000000000..cd4c3d136caa5 --- /dev/null +++ b/x-pack/plugin/sql/qa/server/src/main/resources/logs_unsigned_long.csv @@ -0,0 +1,102 @@ +id,@timestamp,bytes_in,bytes_out,status +1,2017-11-10T21:15:54Z,4348801185987554667,12749081495402663265,OK +2,2017-11-10T21:15:39Z,11054572642507476486,2215793005711196537,OK +3,2017-11-10T21:15:39Z,7239423344688551324,4747671420480199905,OK +4,2017-11-10T21:15:39Z,12880875341157998416,10347160802894727455,OK +5,2017-11-10T21:15:40Z,6480569113728286781,4628689249901172357,OK +6,2017-11-10T21:15:40Z,8847365258155648277,18107197698386620672,OK +7,2017-11-10T21:15:40Z,18081123477485622121,6254036056888007861,OK +8,2017-11-10T21:15:41Z,17159009460398071592,6041947699951197416,OK +9,2017-11-10T21:15:41Z,18317075104972913640,3738987414350619907,OK +10,2017-11-10T20:36:07Z,9223372036854775807,13014552081688587417,OK +11,2017-11-10T20:36:08Z,10618481193158417699,7645257133789254601,OK +12,2017-11-10T20:36:07Z,14243423136348863449,1851693232606252132,OK +13,2017-11-10T20:36:07Z,8014838889043461601,12855878692699288887,OK +14,2017-11-10T20:36:15Z,9704166250476073712,9243820354371174974,OK +15,2017-11-10T20:36:15Z,16673466483681919036,17281501450843634251,OK +16,2017-11-10T20:35:54Z,11414099303186823563,4552407785188434877,OK +17,2017-11-10T20:35:54Z,9614024902524991937,583785103161450865,OK +18,2017-11-10T20:35:55Z,2703254959364209157,15688732125935676003,OK +19,2017-11-10T17:54:43Z,16907772202142018796,1978055896356244912,OK +20,2017-11-10T23:23:24Z,18446744073709551614,9891957732954625161,OK +21,2017-11-10T17:54:59Z,18098466156271475039,10560599221675458546,OK +22,2017-11-10T21:13:27Z,12113814789427553914,17695317925249333633,OK +23,2017-11-10T22:37:41Z,369412756671598363,4454824974559554214,OK +24,2017-11-10T20:34:43Z,17764691215469285192,751496841062464739,OK +25,2017-11-10T23:30:46Z,316080452389500167,13471731928228498458,OK +26,2017-11-10T21:13:16Z,3987249898147090269,857017108209908030,OK +27,2017-11-10T23:36:32Z,9343007301895818617,13652755194722568502,OK +28,2017-11-10T23:36:33Z,12951716972543168268,9336652471323200906,OK +29,2017-11-10T20:35:26Z,16002960716282089759,6754707638562449159,OK +30,2017-11-10T23:36:41Z,18446744073709550591,14393839423240122480,OK +31,2017-11-10T23:56:36Z,5495907774457032585,8384790841458113028,OK +32,2017-11-10T20:29:25Z,905851433235877972,11682551086136399874,Error +33,2017-11-10T21:35:01Z,4368413537705409055,10386906319745215430,OK +34,2017-11-10T21:12:17Z,7953313938735885073,14008282674840239286,OK +35,2017-11-10T23:17:14Z,9188929021194043442,991636820083925493,OK +36,2017-11-10T23:28:11Z,13875498092704386047,17953153966527637143,OK +37,2017-11-10T22:36:27Z,8156660980420095219,901610289258538340,OK +38,2017-11-10T20:35:55Z,2408213296071189837,419872666232023984,OK +39,2017-11-10T20:35:55Z,17460378829280278708,10724795375261191248,OK +40,2017-11-10T20:35:55Z,18446744073709551614,14524142879756567901,OK +41,2017-11-10T20:35:55Z,,,Error +42,2017-11-10T21:34:49Z,154551962150890561,4317649615355527138,Error +43,2017-11-10T20:35:55Z,6713823401157015713,768392740554438381,OK +44,2017-11-10T20:14:04Z,13007085541148329579,1262767764958640849,OK +45,2017-11-10T19:38:06Z,4008445367955620676,2444837981761911481,OK +46,2017-11-10T21:14:18Z,9056948257586320738,3660006000364826492,OK +47,2017-11-10T20:35:56Z,10640542847470647209,3071012467454913482,OK +48,2017-11-10T20:53:05Z,14463699407888333801,16193000254773667372,OK +49,2017-11-10T21:25:42Z,4691003749418709874,16735032755695343779,OK +50,2017-11-10T21:14:44Z,18446744073709551615,8359170160363687272,OK +51,2017-11-10T21:28:34Z,10414368669933920698,17857609920324506371,OK +52,2017-11-10T20:35:55Z,14591698995327831783,837800054257171070,OK +53,2017-11-10T20:15:24Z,9149768745019330607,9934783425401329847,OK +54,2017-11-10T20:35:57Z,5826090293715995525,13263580863583654980,OK +55,2017-11-10T17:14:10Z,15352019942832250739,1498178946494790227,OK +56,2017-11-10T20:35:57Z,9732690250707058359,2520919358333960813,OK +57,2017-11-10T23:22:13Z,8914368988247035466,16187631537609304549,OK +58,2017-11-10T20:32:57Z,8420006392678593250,14938622925960605968,OK +59,2017-11-10T21:24:00Z,17056885385468285787,9973198429366930442,OK +60,2017-11-10T20:35:56Z,9223372036854775808,6620615504579533702,OK +61,2017-11-10T23:43:10Z,2390976293435536689,16020561580624977312,OK +62,2017-11-10T20:35:57Z,10993546521190430203,18184253384683076090,OK +63,2017-11-10T20:21:58Z,5246566629176459718,9382204513185396493,OK +64,2017-11-10T20:35:57Z,9983398877364735609,10626289664367265415,OK +65,2017-11-10T20:33:06Z,5480608687137202404,6895880056122579688,Error +66,2017-11-10T20:35:57Z,7538807943450220608,11745980216826561015,OK +67,2017-11-10T20:26:21Z,17067060651018256448,1722789377000665830,OK +68,2017-11-10T21:23:25Z,16873365461162643186,10056378788277261033,OK +69,2017-11-10T21:23:54Z,9991932520184465636,16110121334900810541,OK +70,2017-11-10T20:35:57Z,0,2507200025082562692,OK +71,2017-11-10T00:27:03Z,0,18223615477147360166,OK +72,2017-11-10T00:27:46Z,0,11206857258468587792,OK +73,2017-11-10T20:35:58Z,13986802678251316321,1330575423003442317,OK +74,2017-11-10T20:35:57Z,13922094693483143156,14343149449348005776,OK +75,2017-11-10T22:27:09Z,13999070515664268533,8422074124513216267,OK +76,2017-11-10T20:35:58Z,15968566213936682639,3784845108080773823,OK +77,2017-11-10T22:26:44Z,1729864283282545225,11105009496753939058,OK +78,2017-11-10T22:27:31Z,14241624006161076477,11563896463355414928,OK +79,2017-11-10T20:35:52Z,2294690022638798960,14564159158999105001,OK +80,2017-11-10T00:00:22Z,0,11060623717086222747,OK +81,2017-11-10T20:35:52Z,7470203340634956368,7490193999241578548,OK +82,2017-11-10T00:01:20Z,74330435873664882,4875216609683497742,OK +83,2017-11-10T00:01:04Z,9636626466125797351,14208813483941526550,OK +84,2017-11-10T00:32:48Z,11949176856304796477,8190769023162854115,OK +85,2017-11-10T00:01:45Z,754822992931077409,12647826153259487490,OK +86,2017-11-10T20:36:08Z,16424089095262982944,12394320926003300611,OK +87,2017-11-10T21:17:37Z,10580536762493152413,13605535835272740587,OK +88,2017-11-10T20:06:49Z,195161570976258241,15395084776572180858,Error +89,2017-11-10T21:17:37Z,15084788733189711518,6353233118260828721,OK +90,2017-11-10T19:51:38Z,,,Error +91,2017-11-10T19:51:38Z,11628588779507401305,8500236459902170712,Error +92,2017-11-10T20:06:50Z,2706408999083639864,594246218266628121,OK +93,2017-11-10T21:17:46Z,9007528787465012783,15931740851225178582,OK +94,2017-11-10T19:51:38Z,18345360876889252152,16119381686035586648,Error +95,2017-11-10T21:17:46Z,2788944430410706777,11087293691148056886,OK +96,2017-11-10T00:04:50Z,9932469097722733505,14925592145374204307,OK +97,2017-11-10T21:17:48Z,11620953158540412267,3809712277266935082,OK +98,2017-11-10T21:12:24Z,3448205404634246112,5409549730889481641,OK +99,2017-11-10T21:17:37Z,1957665857956635540,352442273299370793,OK +100,2017-11-10T03:21:36Z,16462768484251021236,15616395223975497926,OK +101,2017-11-10T23:22:36Z,,,Error diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/multi-cluster-with-security/multi-cluster-command-sys.csv-spec b/x-pack/plugin/sql/qa/server/src/main/resources/multi-cluster-with-security/multi-cluster-command-sys.csv-spec index cf64a70ee74a2..843b7ea3aa056 100644 --- a/x-pack/plugin/sql/qa/server/src/main/resources/multi-cluster-with-security/multi-cluster-command-sys.csv-spec +++ b/x-pack/plugin/sql/qa/server/src/main/resources/multi-cluster-with-security/multi-cluster-command-sys.csv-spec @@ -64,7 +64,8 @@ my_remote_cluster|null |test_emp |last_name.keyword |12 my_remote_cluster|null |test_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO my_remote_cluster|null |test_emp |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO my_remote_cluster|null |test_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO ; sysColumnsWithCatalogAndLike @@ -87,7 +88,8 @@ my_remote_cluster|null |test_emp_copy |last_name.keyword |12 my_remote_cluster|null |test_emp_copy |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO my_remote_cluster|null |test_emp_copy |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO my_remote_cluster|null |test_emp_copy |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO ; sysColumnsOnAliasWithTableLike @@ -110,64 +112,72 @@ my_remote_cluster|null |test_alias |last_name.keyword |12 my_remote_cluster|null |test_alias |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO my_remote_cluster|null |test_alias |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO my_remote_cluster|null |test_alias |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_alias |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_alias |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_alias |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO ; sysColumnsAllTables SYS COLUMNS CATALOG 'my_remote_cluster' TABLE LIKE '%'; - TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | COLUMN_NAME:s | DATA_TYPE:i | TYPE_NAME:s | COLUMN_SIZE:i| BUFFER_LENGTH:i|DECIMAL_DIGITS:i|NUM_PREC_RADIX:i | NULLABLE:i| REMARKS:s | COLUMN_DEF:s |SQL_DATA_TYPE:i|SQL_DATETIME_SUB:i|CHAR_OCTET_LENGTH:i|ORDINAL_POSITION:i|IS_NULLABLE:s|SCOPE_CATALOG:s|SCOPE_SCHEMA:s|SCOPE_TABLE:s|SOURCE_DATA_TYPE:sh|IS_AUTOINCREMENT:s|IS_GENERATEDCOLUMN:s ------------------+---------------+---------------+------------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ -my_remote_cluster|null |logs |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs |bytes_in |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |2 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs |bytes_out |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs |client_ip |12 |IP |45 |45 |null |null |1 |null |null |12 |0 |null |4 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs |client_port |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |5 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs |dest_ip |12 |IP |45 |45 |null |null |1 |null |null |12 |0 |null |6 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |7 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |8 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs_nanos |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs_nanos |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |2 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |logs_nanos |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |3 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |4 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |7 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |8 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |12 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -my_remote_cluster|null |test_emp_copy |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO + TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | COLUMN_NAME:s | DATA_TYPE:i | TYPE_NAME:s | COLUMN_SIZE:i| BUFFER_LENGTH:i|DECIMAL_DIGITS:i|NUM_PREC_RADIX:i | NULLABLE:i| REMARKS:s | COLUMN_DEF:s |SQL_DATA_TYPE:i|SQL_DATETIME_SUB:i|CHAR_OCTET_LENGTH:i|ORDINAL_POSITION:i|IS_NULLABLE:s|SCOPE_CATALOG:s|SCOPE_SCHEMA:s|SCOPE_TABLE:s|SOURCE_DATA_TYPE:sh|IS_AUTOINCREMENT:s|IS_GENERATEDCOLUMN:s +-----------------+---------------+-------------------+------------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ +my_remote_cluster|null |logs |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs |bytes_in |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |2 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs |bytes_out |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs |client_ip |12 |IP |45 |45 |null |null |1 |null |null |12 |0 |null |4 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs |client_port |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |5 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs |dest_ip |12 |IP |45 |45 |null |null |1 |null |null |12 |0 |null |6 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |7 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |8 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs_nanos |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs_nanos |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |2 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs_nanos |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |3 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs_unsigned_long |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs_unsigned_long |bytes_in |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |2 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs_unsigned_long |bytes_out |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |3 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs_unsigned_long |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |4 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |logs_unsigned_long |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |4 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |7 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |8 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |12 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |test_emp_copy |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO ; sysTablesSimple SYS TABLES CATALOG LIKE 'my_remote_cluster'; - TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | TABLE_TYPE:s | REMARKS:s | TYPE_CAT:s | TYPE_SCHEM:S | TYPE_NAME:s |SELF_REFERENCING_COL_NAME:s|REF_GENERATION:s ------------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+-------------------------+--------------- -my_remote_cluster|null |empty_mapping |TABLE | |null |null |null |null |null -my_remote_cluster|null |logs |TABLE | |null |null |null |null |null -my_remote_cluster|null |logs_nanos |TABLE | |null |null |null |null |null -my_remote_cluster|null |test_emp |TABLE | |null |null |null |null |null -my_remote_cluster|null |test_emp_copy |TABLE | |null |null |null |null |null + TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | TABLE_TYPE:s | REMARKS:s | TYPE_CAT:s | TYPE_SCHEM:S | TYPE_NAME:s |SELF_REFERENCING_COL_NAME:s|REF_GENERATION:s +-----------------+---------------+-------------------+---------------+---------------+---------------+---------------+---------------+-------------------------+--------------- +my_remote_cluster|null |empty_mapping |TABLE | |null |null |null |null |null +my_remote_cluster|null |logs |TABLE | |null |null |null |null |null +my_remote_cluster|null |logs_nanos |TABLE | |null |null |null |null |null +my_remote_cluster|null |logs_unsigned_long |TABLE | |null |null |null |null |null +my_remote_cluster|null |test_emp |TABLE | |null |null |null |null |null +my_remote_cluster|null |test_emp_copy |TABLE | |null |null |null |null |null ; sysTablesIndexLikeFilter @@ -186,16 +196,17 @@ SYS TABLES CATALOG LIKE 'my_remote_cluster' "empty*"; my_remote_cluster|null |empty_mapping |TABLE | |null |null |null |null |null ; -sysTablesEmptyFilters +sysTablesEmptyFiltersExceptCatalog SYS TABLES CATALOG LIKE 'my_remote_cluster' "" TYPE ''; - TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | TABLE_TYPE:s | REMARKS:s | TYPE_CAT:s | TYPE_SCHEM:S | TYPE_NAME:s |SELF_REFERENCING_COL_NAME:s|REF_GENERATION:s ------------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+-------------------------+--------------- -my_remote_cluster|null |empty_mapping |TABLE | |null |null |null |null |null -my_remote_cluster|null |logs |TABLE | |null |null |null |null |null -my_remote_cluster|null |logs_nanos |TABLE | |null |null |null |null |null -my_remote_cluster|null |test_emp |TABLE | |null |null |null |null |null -my_remote_cluster|null |test_emp_copy |TABLE | |null |null |null |null |null + TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | TABLE_TYPE:s | REMARKS:s | TYPE_CAT:s | TYPE_SCHEM:S | TYPE_NAME:s |SELF_REFERENCING_COL_NAME:s|REF_GENERATION:s +-----------------+---------------+-------------------+---------------+---------------+---------------+---------------+---------------+-------------------------+--------------- +my_remote_cluster|null |empty_mapping |TABLE | |null |null |null |null |null +my_remote_cluster|null |logs |TABLE | |null |null |null |null |null +my_remote_cluster|null |logs_nanos |TABLE | |null |null |null |null |null +my_remote_cluster|null |logs_unsigned_long |TABLE | |null |null |null |null |null +my_remote_cluster|null |test_emp |TABLE | |null |null |null |null |null +my_remote_cluster|null |test_emp_copy |TABLE | |null |null |null |null |null ; sysTablesCatalogEnumeration diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/multi-cluster-with-security/multi-cluster-command.csv-spec b/x-pack/plugin/sql/qa/server/src/main/resources/multi-cluster-with-security/multi-cluster-command.csv-spec index 6bfc91daee569..2d77fd476c1a2 100644 --- a/x-pack/plugin/sql/qa/server/src/main/resources/multi-cluster-with-security/multi-cluster-command.csv-spec +++ b/x-pack/plugin/sql/qa/server/src/main/resources/multi-cluster-with-security/multi-cluster-command.csv-spec @@ -21,38 +21,41 @@ my_remote_cluster |remote showTables SHOW TABLES CATALOG 'my_remote_cluster'; - catalog | name | type | kind ------------------+---------------+---------------+--------------- -my_remote_cluster|empty_mapping |TABLE |INDEX -my_remote_cluster|logs |TABLE |INDEX -my_remote_cluster|logs_nanos |TABLE |INDEX -my_remote_cluster|test_emp |TABLE |INDEX -my_remote_cluster|test_emp_copy |TABLE |INDEX + catalog | name | type | kind +-----------------+-------------------+---------------+--------------- +my_remote_cluster|empty_mapping |TABLE |INDEX +my_remote_cluster|logs |TABLE |INDEX +my_remote_cluster|logs_nanos |TABLE |INDEX +my_remote_cluster|logs_unsigned_long |TABLE |INDEX +my_remote_cluster|test_emp |TABLE |INDEX +my_remote_cluster|test_emp_copy |TABLE |INDEX ; showTablesWithLike SHOW TABLES CATALOG LIKE 'my_remote_%'; - catalog | name | type | kind ------------------+---------------+---------------+--------------- -my_remote_cluster|empty_mapping |TABLE |INDEX -my_remote_cluster|logs |TABLE |INDEX -my_remote_cluster|logs_nanos |TABLE |INDEX -my_remote_cluster|test_emp |TABLE |INDEX -my_remote_cluster|test_emp_copy |TABLE |INDEX + catalog | name | type | kind +-----------------+-------------------+---------------+--------------- +my_remote_cluster|empty_mapping |TABLE |INDEX +my_remote_cluster|logs |TABLE |INDEX +my_remote_cluster|logs_nanos |TABLE |INDEX +my_remote_cluster|logs_unsigned_long |TABLE |INDEX +my_remote_cluster|test_emp |TABLE |INDEX +my_remote_cluster|test_emp_copy |TABLE |INDEX ; showTablesWithFrozen SHOW TABLES CATALOG 'my_remote_cluster' INCLUDE FROZEN; - catalog | name | type | kind ------------------+---------------+---------------+--------------- -my_remote_cluster|empty_mapping |TABLE |INDEX -my_remote_cluster|frozen_emp |TABLE |INDEX -my_remote_cluster|logs |TABLE |INDEX -my_remote_cluster|logs_nanos |TABLE |INDEX -my_remote_cluster|test_emp |TABLE |INDEX -my_remote_cluster|test_emp_copy |TABLE |INDEX + catalog | name | type | kind +-----------------+-------------------+---------------+--------------- +my_remote_cluster|empty_mapping |TABLE |INDEX +my_remote_cluster|frozen_emp |TABLE |INDEX +my_remote_cluster|logs |TABLE |INDEX +my_remote_cluster|logs_nanos |TABLE |INDEX +my_remote_cluster|logs_unsigned_long |TABLE |INDEX +my_remote_cluster|test_emp |TABLE |INDEX +my_remote_cluster|test_emp_copy |TABLE |INDEX ; showTablesSimpleLike @@ -114,13 +117,14 @@ test_alias_emp |VIEW |ALIAS showTablesAllCatalogs SHOW TABLES CATALOG '*'; - catalog | name | type | kind ------------------+---------------+---------------+--------------- -my_remote_cluster|empty_mapping |TABLE |INDEX -my_remote_cluster|logs |TABLE |INDEX -my_remote_cluster|logs_nanos |TABLE |INDEX -my_remote_cluster|test_emp |TABLE |INDEX -my_remote_cluster|test_emp_copy |TABLE |INDEX + catalog | name | type | kind +-----------------+-------------------+---------------+--------------- +my_remote_cluster|empty_mapping |TABLE |INDEX +my_remote_cluster|logs |TABLE |INDEX +my_remote_cluster|logs_nanos |TABLE |INDEX +my_remote_cluster|logs_unsigned_long |TABLE |INDEX +my_remote_cluster|test_emp |TABLE |INDEX +my_remote_cluster|test_emp_copy |TABLE |INDEX ; // DESCRIBE @@ -153,6 +157,7 @@ last_name.keyword |VARCHAR |keyword name |VARCHAR |keyword null_constant |VARCHAR |keyword salary |INTEGER |integer +salary_ul |NUMERIC |unsigned_long wildcard_name |VARCHAR |keyword ; @@ -184,6 +189,7 @@ last_name.keyword |VARCHAR |keyword name |VARCHAR |keyword null_constant |VARCHAR |keyword salary |INTEGER |integer +salary_ul |NUMERIC |unsigned_long wildcard_name |VARCHAR |keyword ; @@ -303,6 +309,7 @@ last_name.keyword |VARCHAR |keyword name |VARCHAR |keyword null_constant |VARCHAR |keyword salary |INTEGER |integer +salary_ul |NUMERIC |unsigned_long wildcard_name |VARCHAR |keyword ; @@ -334,6 +341,7 @@ last_name.keyword |VARCHAR |keyword name |VARCHAR |keyword null_constant |VARCHAR |keyword salary |INTEGER |integer +salary_ul |NUMERIC |unsigned_long wildcard_name |VARCHAR |keyword ; diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/single-node-only/command-sys.csv-spec b/x-pack/plugin/sql/qa/server/src/main/resources/single-node-only/command-sys.csv-spec index 6963b7b351f77..006b1ffa3e37f 100644 --- a/x-pack/plugin/sql/qa/server/src/main/resources/single-node-only/command-sys.csv-spec +++ b/x-pack/plugin/sql/qa/server/src/main/resources/single-node-only/command-sys.csv-spec @@ -9,17 +9,17 @@ SYS COLUMNS TABLE LIKE 'test\_emp' ESCAPE '\'; TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | COLUMN_NAME:s | DATA_TYPE:i | TYPE_NAME:s | COLUMN_SIZE:i| BUFFER_LENGTH:i|DECIMAL_DIGITS:i|NUM_PREC_RADIX:i | NULLABLE:i| REMARKS:s | COLUMN_DEF:s |SQL_DATA_TYPE:i|SQL_DATETIME_SUB:i|CHAR_OCTET_LENGTH:i|ORDINAL_POSITION:i|IS_NULLABLE:s|SCOPE_CATALOG:s|SCOPE_SCHEMA:s|SCOPE_TABLE:s|SOURCE_DATA_TYPE:sh|IS_AUTOINCREMENT:s|IS_GENERATEDCOLUMN:s -----------------+---------------+---------------+----------------------+---------------+---------------+---------------+---------------+-----------------+-----------------+-----------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ -integTest |null |test_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |4 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |7 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |8 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |12 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |4 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |7 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |8 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |12 |YES |null |null |null |null |NO |NO ; sysColumnsWithTableLikeNoEscape @@ -30,22 +30,23 @@ SYS COLUMNS TABLE LIKE 'test_emp'; TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | COLUMN_NAME:s | DATA_TYPE:i | TYPE_NAME:s | COLUMN_SIZE:i| BUFFER_LENGTH:i|DECIMAL_DIGITS:i|NUM_PREC_RADIX:i | NULLABLE:i| REMARKS:s | COLUMN_DEF:s |SQL_DATA_TYPE:i|SQL_DATETIME_SUB:i|CHAR_OCTET_LENGTH:i|ORDINAL_POSITION:i|IS_NULLABLE:s|SCOPE_CATALOG:s|SCOPE_SCHEMA:s|SCOPE_TABLE:s|SOURCE_DATA_TYPE:sh|IS_AUTOINCREMENT:s|IS_GENERATEDCOLUMN:s ---------------+---------------+---------------+--------------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ -integTest |null |test_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO ; sysColumnsWithCatalogAndLike @@ -53,22 +54,23 @@ SYS COLUMNS CATALOG 'integTest' TABLE LIKE 'test\_emp\_copy' ESCAPE '\'; TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | COLUMN_NAME:s | DATA_TYPE:i | TYPE_NAME:s | COLUMN_SIZE:i| BUFFER_LENGTH:i|DECIMAL_DIGITS:i|NUM_PREC_RADIX:i | NULLABLE:i| REMARKS:s | COLUMN_DEF:s |SQL_DATA_TYPE:i|SQL_DATETIME_SUB:i|CHAR_OCTET_LENGTH:i|ORDINAL_POSITION:i|IS_NULLABLE:s|SCOPE_CATALOG:s|SCOPE_SCHEMA:s|SCOPE_TABLE:s|SOURCE_DATA_TYPE:sh|IS_AUTOINCREMENT:s|IS_GENERATEDCOLUMN:s ---------------+---------------+---------------+-------------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ -integTest |null |test_emp_copy |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO ; sysColumnsOnAliasWithTableLike @@ -76,97 +78,106 @@ SYS COLUMNS TABLE LIKE 'test\_alias' ESCAPE '\'; TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | COLUMN_NAME:s | DATA_TYPE:i | TYPE_NAME:s | COLUMN_SIZE:i| BUFFER_LENGTH:i|DECIMAL_DIGITS:i|NUM_PREC_RADIX:i | NULLABLE:i| REMARKS:s | COLUMN_DEF:s |SQL_DATA_TYPE:i|SQL_DATETIME_SUB:i|CHAR_OCTET_LENGTH:i|ORDINAL_POSITION:i|IS_NULLABLE:s|SCOPE_CATALOG:s|SCOPE_SCHEMA:s|SCOPE_TABLE:s|SOURCE_DATA_TYPE:sh|IS_AUTOINCREMENT:s|IS_GENERATEDCOLUMN:s ---------------+---------------+---------------+--------------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ -integTest |null |test_alias |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO ; sysColumnsAllTables SYS COLUMNS TABLE LIKE '%'; - TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | COLUMN_NAME:s | DATA_TYPE:i | TYPE_NAME:s | COLUMN_SIZE:i|BUFFER_LENGTH:i|DECIMAL_DIGITS:i|NUM_PREC_RADIX:i | NULLABLE:i| REMARKS:s | COLUMN_DEF:s |SQL_DATA_TYPE:i|SQL_DATETIME_SUB:i|CHAR_OCTET_LENGTH:i|ORDINAL_POSITION:i|IS_NULLABLE:s|SCOPE_CATALOG:s|SCOPE_SCHEMA:s|SCOPE_TABLE:s|SOURCE_DATA_TYPE:sh|IS_AUTOINCREMENT:s|IS_GENERATEDCOLUMN:s ------------------+---------------+---------------+------------------+---------------+----------------+---------------+---------------+----------------+---------------+---------------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ -integTest |null |logs |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |logs |bytes_in |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |2 |YES |null |null |null |null |NO |NO -integTest |null |logs |bytes_out |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -integTest |null |logs |client_ip |12 |IP |45 |45 |null |null |1 |null |null |12 |0 |null |4 |YES |null |null |null |null |NO |NO -integTest |null |logs |client_port |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |5 |YES |null |null |null |null |NO |NO -integTest |null |logs |dest_ip |12 |IP |45 |45 |null |null |1 |null |null |12 |0 |null |6 |YES |null |null |null |null |NO |NO -integTest |null |logs |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |7 |YES |null |null |null |null |NO |NO -integTest |null |logs |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |8 |YES |null |null |null |null |NO |NO -integTest |null |logs_nanos |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |logs_nanos |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |2 |YES |null |null |null |null |NO |NO -integTest |null |logs_nanos |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |3 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -integTest |null |test_alias |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -integTest |null |test_alias_emp |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |4 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |7 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |8 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -integTest |null |test_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |12 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO -integTest |null |test_emp_copy |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |19 |YES |null |null |null |null |NO |NO + TABLE_CAT:s | TABLE_SCHEM:s| TABLE_NAME:s | COLUMN_NAME:s | DATA_TYPE:i | TYPE_NAME:s | COLUMN_SIZE:i|BUFFER_LENGTH:i|DECIMAL_DIGITS:i|NUM_PREC_RADIX:i | NULLABLE:i| REMARKS:s | COLUMN_DEF:s |SQL_DATA_TYPE:i|SQL_DATETIME_SUB:i|CHAR_OCTET_LENGTH:i|ORDINAL_POSITION:i|IS_NULLABLE:s|SCOPE_CATALOG:s|SCOPE_SCHEMA:s|SCOPE_TABLE:s|SOURCE_DATA_TYPE:sh|IS_AUTOINCREMENT:s|IS_GENERATEDCOLUMN:s +-----------------+---------------+-------------------+------------------+---------------+----------------+---------------+---------------+----------------+---------------+---------------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ +integTest |null |logs |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |logs |bytes_in |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |2 |YES |null |null |null |null |NO |NO +integTest |null |logs |bytes_out |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |logs |client_ip |12 |IP |45 |45 |null |null |1 |null |null |12 |0 |null |4 |YES |null |null |null |null |NO |NO +integTest |null |logs |client_port |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |5 |YES |null |null |null |null |NO |NO +integTest |null |logs |dest_ip |12 |IP |45 |45 |null |null |1 |null |null |12 |0 |null |6 |YES |null |null |null |null |NO |NO +integTest |null |logs |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |7 |YES |null |null |null |null |NO |NO +integTest |null |logs |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |8 |YES |null |null |null |null |NO |NO +integTest |null |logs_nanos |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |logs_nanos |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |2 |YES |null |null |null |null |NO |NO +integTest |null |logs_nanos |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |3 |YES |null |null |null |null |NO |NO +integTest |null |logs_unsigned_long |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |logs_unsigned_long |bytes_in |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |2 |YES |null |null |null |null |NO |NO +integTest |null |logs_unsigned_long |bytes_out |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |logs_unsigned_long |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |4 |YES |null |null |null |null |NO |NO +integTest |null |logs_unsigned_long |status |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +integTest |null |test_alias |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +integTest |null |test_alias_emp |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |4 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |7 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |8 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +integTest |null |test_emp |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |12 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |birth_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |emp_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |extra.info.gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |6 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |extra_gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |7 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |extra_no |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |8 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |first_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |9 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |first_name.keyword|12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |10 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |gender |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |11 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |hire_date |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |12 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |languages |-6 |BYTE |5 |1 |null |10 |1 |null |null |-6 |0 |null |13 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |last_name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |14 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |last_name.keyword |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |15 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |16 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |null_constant |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |17 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |salary |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |18 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |salary_ul |2 |UNSIGNED_LONG |20 |8 |null |10 |1 |null |null |2 |0 |null |19 |YES |null |null |null |null |NO |NO +integTest |null |test_emp_copy |wildcard_name |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |20 |YES |null |null |null |null |NO |NO ; diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/slow/frozen.csv-spec b/x-pack/plugin/sql/qa/server/src/main/resources/slow/frozen.csv-spec index 2b5da3da451f5..b68d6e49443f1 100644 --- a/x-pack/plugin/sql/qa/server/src/main/resources/slow/frozen.csv-spec +++ b/x-pack/plugin/sql/qa/server/src/main/resources/slow/frozen.csv-spec @@ -12,6 +12,7 @@ integTest |empty_mapping |TABLE |INDEX integTest |frozen_emp |TABLE |FROZEN INDEX integTest |logs |TABLE |INDEX integTest |logs_nanos |TABLE |INDEX +integTest |logs_unsigned_long |TABLE |INDEX integTest |test_alias |VIEW |ALIAS integTest |test_alias_emp |VIEW |ALIAS integTest |test_emp |TABLE |INDEX diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/unsigned-long.csv-spec b/x-pack/plugin/sql/qa/server/src/main/resources/unsigned-long.csv-spec new file mode 100644 index 0000000000000..2b4df08dc7618 --- /dev/null +++ b/x-pack/plugin/sql/qa/server/src/main/resources/unsigned-long.csv-spec @@ -0,0 +1,693 @@ +// To mute tests follow example in file: example.csv-spec + +// +// Unsigned long tests +// + +arithmeticUnsignedLongCast +SELECT 1::unsigned_long + 1 AS x; + + x:ul +--------------- +2 +; + +nullArithmetics +SELECT null + 18446744073709551614 AS x; + + x:ul +--------------- +null +; + +arithmeticPlus +SELECT 18446744073709551614 + 1 AS x; + + x:ul +--------------- +18446744073709551615 +; + +arithmeticMultiply +SELECT 9223372036854775807::unsigned_long * 2 AS x; + + x:ul +--------------- +18446744073709551614 +; + +arithmeticDivide +SELECT 18446744073709551614 / 2 AS x; + + x:ul +--------------- +9223372036854775807 +; + +arithmeticModulo +SELECT 18446744073709551614 % 10 AS x; + + x:ul +--------------- +4 +; + +selectWithOrderByAndComparison +SELECT salary_ul AS x FROM test_emp_copy WHERE salary_ul > 70000 ORDER by salary_ul DESC LIMIT 1; + + x:ul +--------------- +74999 +; + +selectWithImplicitAgg +SELECT MAX(salary_ul)::STRING AS x FROM test_emp_copy; + + x:s +--------------- +74999 +; + +selectWithGroupByCountAndOrderBy +SELECT salary_ul AS s, count(1) AS c FROM test_emp_copy GROUP BY 1 ORDER BY 1 LIMIT 3; + + s:ul | c:l +---------------+--------------- +25324 |1 +25945 |1 +25976 |1 +; + +selectWithMathFunction +SELECT ROUND(SIN(salary_ul), 3) AS sin, salary_ul AS sal FROM test_emp_copy ORDER BY sal DESC LIMIT 3; + + sin:d | sal:ul +---------------+--------------- +0.239 |74999 +-0.823 |74970 +-0.015 |74572 +; + +selectWithIn +SELECT salary_ul, salary_ul + languages AS s, languages FROM test_emp_copy WHERE salary_ul IN ('74999', 74970, 74572::unsigned_long); + + salary_ul:ul | s:ul | languages:bt +------------------+---------------+--------------- +74572 |74576 |4 +74999 |null |null +74970 |74973 |3 +; + +countWithImplicitGroupBy +SELECT MAX(salary_ul) AS m FROM test_emp_copy ORDER BY COUNT(*); + + m:ul +--------------- +74999 +; + +kurtosisAndSkewnessGroup +SELECT gender, KURTOSIS(salary_ul) k, SKEWNESS(salary_ul) s FROM test_emp_copy GROUP BY gender; + +gender:s | k:d | s:d + +null |2.2215791166941923 |-0.03373126000214023 +F |1.7873117044424276 |0.05504995122217512 +M |2.280646181070106 |0.44302407229580243 +; + +sumFieldWithSumLiteralAsCondition +SELECT first_name, last_name, SUM(salary_ul) AS s, birth_date AS y, COUNT(1) FROM test_emp_copy GROUP BY 1, 2, 4 HAVING ((SUM(1) >= 1) AND (SUM(1) <= 577)) AND ((SUM(salary_ul) >= 35000) AND (SUM(salary_ul) <= 45000)); + + first_name:s | last_name:s | s:d | y:ts | COUNT(1):l +---------------+---------------+---------------+------------------------+--------------- +null |Brender |36051 |1959-10-01T00:00:00.000Z|1 +null |Joslin |37716 |1959-01-27T00:00:00.000Z|1 +null |Lortz |35222 |1960-07-20T00:00:00.000Z|1 +null |Makrucki |37691 |1963-07-22T00:00:00.000Z|1 +null |Swan |39878 |1962-12-29T00:00:00.000Z|1 +Alejandro |McAlpine |44307 |1953-09-19T00:00:00.000Z|1 +Amabile |Gomatam |38645 |1955-10-04T00:00:00.000Z|1 +Basil |Tramer |37853 |null |1 +Berhard |McFarlin |38376 |1954-10-01T00:00:00.000Z|1 +Berni |Genin |37137 |1956-02-12T00:00:00.000Z|1 +Chirstian |Koblick |36174 |1954-05-01T00:00:00.000Z|1 +Domenick |Tempesti |39356 |1963-11-26T00:00:00.000Z|1 +Hilari |Morton |37702 |1965-01-03T00:00:00.000Z|1 +Hisao |Lipner |40612 |1958-01-21T00:00:00.000Z|1 +Jayson |Mandell |43889 |1954-09-16T00:00:00.000Z|1 +Jungsoon |Syrzycki |39638 |1954-02-25T00:00:00.000Z|1 +Kendra |Hofting |44956 |1961-05-30T00:00:00.000Z|1 +Kenroku |Malabarba |35742 |1962-11-07T00:00:00.000Z|1 +Margareta |Bierman |41933 |1960-09-06T00:00:00.000Z|1 +Mayuko |Warwick |40031 |1952-12-24T00:00:00.000Z|1 +Mingsen |Casley |39728 |null |1 +Mokhtar |Bernatsky |38992 |1955-08-28T00:00:00.000Z|1 +Saniya |Kalloufi |43906 |1958-02-19T00:00:00.000Z|1 +Sreekrishna |Servieres |44817 |1961-09-23T00:00:00.000Z|1 +Sudharsan |Flasterstein |43602 |1963-03-21T00:00:00.000Z|1 +Vishv |Zockler |39110 |1959-07-23T00:00:00.000Z|1 +Weiyi |Meriste |37112 |null |1 +Yinghua |Dredge |43026 |1958-05-21T00:00:00.000Z|1 +Zvonko |Nyanchama |42716 |null |1 +; + +histogramNumeric +SELECT HISTOGRAM(salary_ul, 5000) AS h FROM test_emp_copy GROUP BY h; + + h:ul +--------------- +25000 +30000 +35000 +40000 +45000 +50000 +55000 +60000 +65000 +70000 +; + +medianAbsoluteDeviation +schema::gender:s|mad:d +SELECT gender, MAD(salary_ul) AS mad FROM test_emp_copy GROUP BY gender ORDER BY gender; + + gender | mad +---------------+--------------- +null |10789.0 +F |12719.0 +M |8905.0 +; + +groupAndAggNotSpecifiedInTheAggregateWithHaving +SELECT gender, MIN(salary_ul) AS min, COUNT(*) AS c FROM test_emp_copy GROUP BY gender HAVING c > 1 ORDER BY gender NULLS FIRST, MAX(salary_ul); + + gender:s | min:ul | c:l +---------------+---------------+--------------- +null |25324 |10 +F |25976 |33 +M |25945 |57 +; + +aggNotSpecifiedWithHavingOnLargeGroupBy +SELECT MAX(salary_ul) AS max FROM test_emp_copy GROUP BY emp_no HAVING AVG(salary_ul) > 1000 ORDER BY MIN(salary_ul) LIMIT 5; + + max:ul +--------------- +25324 +25945 +25976 +26436 +27215 +; + +multipleGroupingsAndOrderingByGroupsAndAggs +SELECT gender, MIN(salary_ul + 1) AS min, COUNT(*) AS c, MAX(salary_ul) AS max FROM test_emp_copy GROUP BY gender HAVING c > 1 ORDER BY gender DESC NULLS LAST, MAX(salary_ul) ASC; + + gender:s | min:ul | c:l | max:ul +---------------+---------------+---------------+--------------- +M |25946 |57 |74999 +F |25977 |33 |74572 +null |25325 |10 |73717 +; + +aggSumWithAliasWithColumnRepeatedWithOrderDesc +SELECT gender AS g, gender, SUM(salary_ul) AS s3, SUM(salary_ul), SUM(salary_ul) AS s5 FROM test_emp_copy GROUP BY g ORDER BY s5 DESC; + +g:s | gender:s | s3:d | SUM(salary_ul):d | s5:d +-----+-----------+-------+-------------------+------ +M |M |2671054|2671054 |2671054 +F |F |1666196|1666196 |1666196 +null |null |487605 |487605 |487605 +; + +aggregateFunctionsWithScalars +SELECT MAX(CASE WHEN (salary_ul - 10) > 70000 THEN (salary_ul + 12345) * 1.2 ELSE (salary_ul - 12345) * 2.7 END) AS "max", +MIN(CASE WHEN (salary_ul - 20) > 50000 THEN (salary_ul * 1.2) - 1234 ELSE (salary_ul - 20) * 0.93 END) AS "min", +AVG(COS(salary_ul * 1.2) + 100 * (salary_ul / 5)) AS "avg", +SUM(salary_ul / 0.765 + sin((salary_ul + 12345) / 12)) AS "sum", +MAD(ABS(salary_ul / -0.813) / 2 + (12345 * (salary_ul % 10))) AS "mad" +FROM test_emp_copy; + + max | min | avg | sum | mad +------------------+---------------+-----------------+-----------------+----------------- +155409.30000000002|23532.72 |964937.9295477575|6306995.482492277|30811.76199261993 +; + +aggregatesWithScalarsAndGroupByOrderByAggWithoutProjection +schema::gender:s +SELECT gender FROM test_emp_copy GROUP BY gender ORDER BY MAX(salary_ul % 100) DESC; + + gender +--------------- +M +null +F +; + +percentileAggregateFunctionsWithScalars +schema::percentile:d|percentile_rank:d|gender:s +SELECT PERCENTILE(CASE WHEN (salary_ul / 2) > 10000 THEN (salary_ul + 12345) * 1.2 ELSE (salary_ul - 12345) * 2.7 END, 80) AS "percentile", +PERCENTILE_RANK(CASE WHEN (salary_ul - 20) > 50000 THEN (salary_ul * 1.2) - 1234 ELSE (salary_ul - 20) * 0.93 END, 40000) AS "percentile_rank", +gender FROM test_emp_copy +GROUP BY gender ORDER BY gender; + + percentile | percentile_rank | gender +-----------------+------------------+--------------- +86857.79999999999|32.69659025378865 |null +94042.92000000001|37.03569653103581 |F +87348.36 |44.337514210592246|M +; + +extendedStatsAggregateFunctionsWithScalars +schema::stddev_pop:d|stddev_samp:d|sum_of_squares:d|var_pop:d|var_samp:d|gender:s +SELECT STDDEV_POP(CASE WHEN (salary_ul / 2) > 10000 THEN (salary_ul + 12345) * 1.2 ELSE (salary_ul - 12345) * 2.7 END) AS "stddev_pop", +STDDEV_SAMP(CASE WHEN (salary_ul / 2) > 10000 THEN (salary_ul + 12345) * 1.2 ELSE (salary_ul - 12345) * 2.7 END) AS "stddev_samp", +SUM_OF_SQUARES(CASE WHEN (salary_ul - 20) > 50000 THEN (salary_ul * 1.2) - 1234 ELSE (salary_ul - 20) * 0.93 END) AS "sum_of_squares", +VAR_POP(CASE WHEN (salary_ul - 20) % 1000 > 200 THEN (salary_ul * 1.2) - 1234 ELSE (salary_ul - 20) * 0.93 END) AS "var_pop", +VAR_SAMP(CASE WHEN (salary_ul - 20) % 1000 > 200 THEN (salary_ul * 1.2) - 1234 ELSE (salary_ul - 20) * 0.93 END) AS "var_samp", +gender FROM test_emp_copy +GROUP BY gender ORDER BY gender; + + stddev_pop | stddev_samp | sum_of_squares | var_pop | var_samp | gender +------------------+------------------+---------------------+--------------------+--------------------+--------------- +16752.73244172422 |17658.930515747525|3.06310583829007E10 |3.460331137445282E8 |3.844812374939202E8 |null +17427.462400181845|17697.67172930331 |1.148127725047658E11 |3.1723426960671306E8|3.271478405319228E8 |F +15702.798665784752|15842.381843421828|1.5882243113919238E11|2.529402043805585E8 |2.5745699374449703E8|M +; + +groupByRoundAndTruncateWithTwoParams +SELECT ROUND(SIN(TRUNCATE("salary_ul", 2)), 2) FROM "test_emp_copy" GROUP BY ROUND(SIN(TRUNCATE("salary_ul", 2)), 2) ORDER BY ROUND(SIN(TRUNCATE("salary_ul", 2)), 2) LIMIT 5; + +ROUND(SIN(TRUNCATE("salary_ul", 2)), 2) +--------------------------------------- +-1.0 +-0.99 +-0.98 +-0.97 +-0.96 +; + +sumLiteralAndSumFieldWithComplexHaving +SELECT gender, CAST(SUM("salary_ul") AS BIGINT), CAST(SUM(1) AS BIGINT), CAST(SUM(10) AS BIGINT), COUNT(*) FROM test_emp_copy GROUP BY gender HAVING ((SUM(1) >= 0) AND (SUM(1) <= 50) AND (SUM(salary_ul) >= 250000) AND (SUM(salary_ul) <= 5000000)) ORDER BY gender; + + gender:s |CAST(SUM("salary_ul") AS BIGINT):l|CAST(SUM(1) AS BIGINT):l|CAST(SUM(10) AS BIGINT):l| COUNT(*):l +---------------+----------------------------------+------------------------+-------------------------+--------------- +null |487605 |10 |100 |10 +F |1666196 |33 |330 |33 +; + +aggGroupByOnScalarWithHaving +SELECT salary_ul + 1 AS e FROM test_emp_copy GROUP BY e HAVING AVG(salary_ul) BETWEEN 50000 AND 60000 ORDER BY e LIMIT 2; + + e:ul +--------------- +50065 +50129 +; + +caseGroupByProtectedDivisionByZero +schema::x:ul +SELECT CASE WHEN languages = 1 THEN NULL ELSE ( salary_ul / (languages - 1) ) END AS x FROM test_emp_copy GROUP BY 1 ORDER BY 1 LIMIT 10; + + x +--------------- +null +6331 +6486 +7780 +7974 +8068 +8489 +8935 +9043 +9071 +; + +iifWithCompatibleIntervals +schema::hire_date + IIF(salary_ul > 70000, INTERVAL 2 HOURS, INTERVAL 2 DAYS):ts|salary_ul:ul +SELECT hire_date + IIF(salary_ul > 70000, INTERVAL 2 HOURS, INTERVAL 2 DAYS), salary_ul FROM test_emp_copy ORDER BY salary DESC LIMIT 10; + +hire_date + IIF(salary_ul > 70000, INTERVAL 2 HOURS, INTERVAL 2 DAYS)| salary_ul +---------------------------------------------------------------------+--------------- +1985-11-20T02:00:00.000Z |74999 +1989-09-02T02:00:00.000Z |74970 +1989-02-10T02:00:00.000Z |74572 +1989-07-07T02:00:00.000Z |73851 +1999-04-30T02:00:00.000Z |73717 +1988-10-18T02:00:00.000Z |73578 +1990-09-15T02:00:00.000Z |71165 +1987-03-18T02:00:00.000Z |70011 +1987-05-28T00:00:00.000Z |69904 +1990-02-18T00:00:00.000Z |68547 +; + +aggPercentile +SELECT languages, PERCENTILE(salary_ul, 95) AS "95th" FROM test_emp_copy GROUP BY languages; + + languages:bt| 95th:d +---------------+----------------- +null |74999.0 +1 |72790.5 +2 |71924.70000000001 +3 |73638.25 +4 |72115.59999999999 +5 |61071.7 +; + +mathPowerNegative +SELECT POWER(salary_ul, -1) m, first_name FROM "test_emp_copy" WHERE emp_no < 10010 ORDER BY emp_no; + + m | first_name +---------------------+--------------- +1.7450484250937964E-5|Georgi +1.773961788863068E-5 |Bezalel +1.617992071838848E-5 |Parto +2.76441643169127E-5 |Chirstian +1.57410905427528E-5 |Kyoichi +1.6574127786525235E-5|Anneke +1.3409858928284075E-5|Tzvetan +2.2775930396756706E-5|Saniya +1.5111675280321576E-5|Sumant +; + +averageWithOneValueAndOrder +schema::languages:bt|'F':d +SELECT * FROM (SELECT languages, gender, salary_ul FROM test_emp_copy) PIVOT (AVG(salary_ul + 1) FOR gender IN ('F')) ORDER BY languages DESC LIMIT 4; + languages | 'F' +---------------+------------------ +5 |46706.555555555555 +4 |49292.5 +3 |53661.0 +2 |50685.4 +; + +sumWithInnerAggregateSumOfSquaresRound +schema::birth_date:ts|emp_no:i|extra.info.gender:s|extra_gender:s|extra_no:i|first_name:s|gender:s|hire_date:ts|last_name:s|name:s|null_constant:s|salary:i|wildcard_name:s|1:d|2:d +SELECT * FROM test_emp_copy PIVOT (ROUND(SUM_OF_SQUARES(salary_ul + 1)/1E6, 2) FOR languages IN (1, 2)) LIMIT 3; + + birth_date | emp_no |extra.info.gender| extra_gender | extra_no | first_name | gender | hire_date | last_name | name | null_constant | salary | wildcard_name | 1 | 2 +---------------+---------------+-----------------+---------------+---------------+---------------+---------------+------------------------+---------------+---------------+---------------+---------------+---------------+---------------+--------------- +null |10041 |F |Female |10041 |Uri |F |1989-11-12T00:00:00.000Z|Lenart |Uri Lenart |null |56415 |Uri Lenart |3182.77 |null +null |10043 |M |Female |10043 |Yishay |M |1990-10-20T00:00:00.000Z|Tzvieli |Yishay Tzvieli |null |34341 |Yishay Tzvieli |1179.37 |null +null |10044 |F |Female |10044 |Mingsen |F |1994-05-21T00:00:00.000Z|Casley |Mingsen Casley |null |39728 |Mingsen Casley |1578.39 |null +; + + +implicitAggAndCastMaxLargeInput +SELECT MAX(bytes_out)::DOUBLE xd, MAX(bytes_in)::STRING xs FROM logs_unsigned_long; + + xd:d | xs:s +--------------------+-------------------- +1.822361547714736E19|18446744073709551615 +; + +implicitAggFirstLast +SELECT FIRST(bytes_in)::BYTE xd, FIRST(bytes_out)::LONG xs FROM logs_unsigned_long; + + xd:bt | xs:l +---------------+------------------ +0 |352442273299370793 +; + +withMathFunctionLargeInput +SELECT ROUND(SIN(bytes_out), 3) AS sin, bytes_out AS out FROM logs_unsigned_long ORDER BY sin DESC NULLS LAST LIMIT 3; + + sin:d | out:ul +---------------+-------------------- +0.999 |8422074124513216267 +0.999 |11563896463355414928 +0.997 |16110121334900810541 +; + +withMathIntoInLargeInput +SELECT id, bytes_in, bytes_out, + ((bytes_out::DOUBLE + CASE WHEN status = 'OK' THEN bytes_in ELSE 0 END)/POWER(1024, 3))::UNSIGNED_LONG AS traffic_gb +FROM logs_unsigned_long +WHERE bytes_in IN (9223372036854775807::UNSIGNED_LONG * 2, 18446744073709551614, '18446744073709551615'::UNSIGNED_LONG); + + id:i | bytes_in:ul | bytes_out:ul | traffic_gb:ul +---------------+--------------------+--------------------+--------------- +20 |18446744073709551614|9891957732954625161 |26392472727 +40 |18446744073709551614|14524142879756567901|30706531324 +50 |18446744073709551615|8359170160363687272 |24964953059 +; + +countWithImplicitGroupByAndArithmeticLargeInput +SELECT MAX(bytes_out) max, MIN(bytes_out) min, MAX(bytes_out) - MIN(bytes_out) diff FROM logs_unsigned_long ORDER BY COUNT(*); + + max:ul | min:ul | diff:ul +--------------------+------------------+-------------------- +18223615477147360166|352442273299370793|17871173203847989373 + +; + +kurtosisAndSkewnessGroupLargeInput +SELECT status, KURTOSIS(bytes_in) k, SKEWNESS(bytes_in) s FROM logs_unsigned_long GROUP BY status; + + status | k | s +---------------+------------------+-------------------- +Error |2.056501455580364 |0.7571349191844446 +OK |1.9651462247673082|-0.15628563062367107 +; + + +fieldWithSumLiteralAsConditionLargeInput +SELECT status, SUM(bytes_in) AS s, "@timestamp" AS y, COUNT(1) FROM logs_unsigned_long GROUP BY 1, 3 HAVING SUM(1) >= 3; + + status:s | s:d | y:ts | COUNT(1):l +---------------+---------------------+------------------------+--------------- +Error |2.9973949656396653E19|2017-11-10T19:51:38.000Z|3 +OK |6.232411355491008E19 |2017-11-10T20:35:55.000Z|6 +OK |5.799662857991158E19 |2017-11-10T20:35:57.000Z|7 +OK |3.14816340622471E19 |2017-11-10T20:36:07.000Z|3 +OK |3.1174871328354025E19|2017-11-10T21:15:39.000Z|3 +OK |3.3409057849369555E19|2017-11-10T21:15:40.000Z|3 +OK |2.76229913536395E19 |2017-11-10T21:17:37.000Z|3 +; + +histogramNumericLargeInput +SELECT HISTOGRAM(bytes_in, POWER(10, 9)) AS gb FROM logs_unsigned_long GROUP BY 1 LIMIT 10; + + gb:ul +------------------- +null +0 +74330435000000000 +154551962000000000 +195161570000000000 +316080452000000000 +369412756000000000 +754822992000000000 +905851433000000000 +1729864283000000000 +; + +medianAbsoluteDeviationLargeInput +SELECT status, MAD(bytes_in) AS mad FROM logs_unsigned_long GROUP BY status ORDER BY status; + + status | mad +---------------+--------------------- +Error |3.0183732936229658E18 +OK |4.5240953206634045E18 +; + + +groupAndAggNotSpecifiedInTheAggregateWithHavingLargeInput +SELECT status, MIN(bytes_out) AS min, COUNT(*) AS c FROM logs_unsigned_long GROUP BY 1 HAVING c > 1 ORDER BY 1 NULLS FIRST, MAX(bytes_out); + + status:s | min:ul | c:l +---------------+-------------------+--------------- +Error |4317649615355527138|9 +OK |352442273299370793 |92 +; + +aggNotSpecifiedWithHavingOnLargeGroupByLargeInput +SELECT MAX(bytes_in) AS max FROM logs_unsigned_long GROUP BY id HAVING AVG(bytes_in) > 1000 ORDER BY MIN(bytes_out) LIMIT 5; + + max:ul +-------------------- +1957665857956635540 +2408213296071189837 +9614024902524991937 +2706408999083639864 +17764691215469285192 +; + +multipleGroupingsAndOrderingByGroupsAndAggsLargeInput +SELECT status, MIN(bytes_out + 1) AS min, COUNT(*) AS c, MAX(bytes_in) AS max FROM logs_unsigned_long GROUP BY status HAVING c > 1 ORDER BY status DESC NULLS LAST, MAX(bytes_in) ASC; + + status:s | min:ul | c:l | max:ul +---------------+------------------+---------------+-------------------- +OK |352442273299370794|92 |18446744073709551615 +Error |null |9 |18345360876889252152 +; + +aggSumWithAliasWithColumnRepeatedWithOrderDescLargeInput +SELECT status AS s, status, SUM(bytes_in) AS s3, SUM(bytes_in), SUM(bytes_in) AS s5 FROM logs_unsigned_long GROUP BY s ORDER BY s5 DESC; + + s:s | status:s | s3:d | SUM(bytes_in):d | s5:d +---------------+---------------+--------------------+--------------------+-------------------- +OK |OK |9.045581107970589E20|9.045581107970589E20|9.045581107970589E20 +Error |Error |3.671012330989688E19|3.671012330989688E19|3.671012330989688E19 +; + +aggregateFunctionsWithScalarsLargeInput + +SELECT MAX(CASE WHEN (bytes_out - 10) > 9223372036854775807 THEN (bytes_out + 12345) * 1.2 ELSE (bytes_out - 12345) * 2.7 END) AS max, +MIN(CASE WHEN (bytes_out - 20) > 9223372036854775807 THEN (bytes_out * 1.2) - 1234 ELSE (bytes_out - 20) * 0.93 END) AS min, +AVG((COS(bytes_out * 1.2) + 100) * (bytes_out / 5)) AS avg, +SUM(bytes_out / 0.765 + sin((bytes_out + 12345) / 12)) AS sum, +MAD(ABS(bytes_out / -0.813) / 2 + (12345 * (bytes_out % 10))) AS mad +FROM logs_unsigned_long; + + max:d | min:d | avg:d | sum:d | mad:d +--------------------+---------------------+--------------------+---------------------+--------------------- +2.295063844173583E19|3.2777131416841485E17|1.820279192886511E20|1.1661879109640491E21|3.0865540937819786E18 +; + + +aggregatesWithScalarsAndGroupByOrderByAggWithoutProjectionLargeInput +SELECT status, MAX(bytes_in % 100) max FROM logs_unsigned_long GROUP BY status ORDER BY 2 DESC; + + status:s | max:ul +---------------+--------------- +OK |99 +Error |72 +; + +percentileAggregateFunctionsWithScalarsLargeInput +SELECT PERCENTILE(CASE WHEN (bytes_out / 2) > 4E18 THEN (bytes_out + 12345) * 1.2 ELSE (bytes_out - 12345) * 2.7 END, 80) AS percentile, +PERCENTILE_RANK(CASE WHEN (bytes_out - 20) > 4E18 THEN (bytes_out * 1.2) - 1234 ELSE (bytes_out - 20) * 0.93 END, 4E18) AS percentile_rank, +status +FROM logs_unsigned_long +GROUP BY status +ORDER BY status; + + percentile | percentile_rank | status +---------------------+------------------+--------------- +1.8836190713044468E19|1.970336796004502 |Error +1.7957483822449326E19|26.644793296251386|OK +; + +extendedStatsAggregateFunctionsWithScalarsLargeInput +SELECT STDDEV_POP(CASE WHEN (bytes_out / 2) > 10000 THEN (bytes_out + 12345) * 1.2 ELSE (bytes_out - 12345) * 2.7 END) AS stddev_pop, +STDDEV_SAMP(CASE WHEN (bytes_out / 2) > 10000 THEN (bytes_out + 12345) * 1.2 ELSE (bytes_out - 12345) * 2.7 END) AS stddev_samp, +SUM_OF_SQUARES(CASE WHEN (bytes_out - 20) > 50000 THEN (bytes_out * 1.2) - 1234 ELSE (bytes_out - 20) * 0.93 END) AS sum_of_squares, +VAR_POP(CASE WHEN (bytes_out - 20) % 1000 > 200 THEN (bytes_out * 1.2) - 1234 ELSE (bytes_out - 20) * 0.93 END) AS var_pop, +VAR_SAMP(CASE WHEN (bytes_out - 20) % 1000 > 200 THEN (bytes_out * 1.2) - 1234 ELSE (bytes_out - 20) * 0.93 END) AS var_samp, +status +FROM logs_unsigned_long +GROUP BY status +ORDER BY status; + + stddev_pop | stddev_samp | sum_of_squares | var_pop | var_samp | status +---------------------+---------------------+---------------------+---------------------+--------------------+--------------- +5.1879845114777528E18|5.6831522898475694E18|1.1113551085273773E39|2.9979868907159907E37|3.597584268859189E37|Error +6.7116742512203459E18|6.7484508237837148E18|1.4906887301541836E40|4.304729535850427E37 |4.352034256024607E37|OK +; + +groupByRoundAndTruncateWithTwoParamsLargeInput +SELECT ROUND(SIN(TRUNCATE(bytes_in, 2)), 2) rst FROM logs_unsigned_long GROUP BY 1 ORDER BY 1 LIMIT 5; + + rst:d +--------------- +null +-1.0 +-0.99 +-0.97 +-0.96 +; + +sumLiteralAndSumFieldWithComplexHavingLargeInput +SELECT status, CAST(SUM(bytes_out) AS STRING), CAST(SUM(1) AS BIGINT), CAST(SUM(10) AS BIGINT), COUNT(*) FROM logs_unsigned_long GROUP BY status HAVING ((SUM(1) >= 0) AND (SUM(1) <= 50) AND (SUM(bytes_out) >= 250000) AND (SUM(bytes_out) <= 7E19)) ORDER BY status; + + status:s |CAST(SUM(bytes_out) AS STRING):s|CAST(SUM(1) AS BIGINT):l|CAST(SUM(10) AS BIGINT):l| COUNT(*):l +---------------+--------------------------------+------------------------+-------------------------+--------------- +Error |6.2910783680124445E19 |9 |90 |9 +; + +aggGroupByOnScalarWithHavingLargeInput +SELECT bytes_out + 1 AS e FROM logs_unsigned_long GROUP BY e HAVING AVG(bytes_out) BETWEEN 9E18 AND 1E19 ORDER BY e LIMIT 2; + + e:ul +------------------- +9243820354371174975 +9336652471323200907 +; + +iifWithCompatibleIntervalsLargeInput +SELECT "@timestamp" ts, "@timestamp" + IIF(bytes_out > 9E18, INTERVAL 2 HOURS, INTERVAL 2 DAYS) ts_plus, bytes_out FROM logs_unsigned_long ORDER BY bytes_out DESC LIMIT 10; + + ts:ts | ts_plus:ts | bytes_out:ul +------------------------+------------------------+-------------------- +2017-11-10T20:35:55.000Z|2017-11-12T20:35:55.000Z|null +2017-11-10T19:51:38.000Z|2017-11-12T19:51:38.000Z|null +2017-11-10T23:22:36.000Z|2017-11-12T23:22:36.000Z|null +2017-11-10T00:27:03.000Z|2017-11-10T02:27:03.000Z|18223615477147360166 +2017-11-10T20:35:57.000Z|2017-11-10T22:35:57.000Z|18184253384683076090 +2017-11-10T21:15:40.000Z|2017-11-10T23:15:40.000Z|18107197698386620672 +2017-11-10T23:28:11.000Z|2017-11-11T01:28:11.000Z|17953153966527637143 +2017-11-10T21:28:34.000Z|2017-11-10T23:28:34.000Z|17857609920324506371 +2017-11-10T21:13:27.000Z|2017-11-10T23:13:27.000Z|17695317925249333633 +2017-11-10T20:36:15.000Z|2017-11-10T22:36:15.000Z|17281501450843634251 +; + +aggPercentileLargeInput +SELECT status, PERCENTILE(bytes_in, 95) AS "95th" FROM logs_unsigned_long GROUP BY status; + + status:s | 95th:d +---------------+--------------------- +Error |1.8345360876889252E19 +OK |1.8295214210102768E19 +; + +mathPowerNegativeLargeInput +SELECT POWER(bytes_in, -1) m, id FROM logs_unsigned_long WHERE id < 10 ORDER BY id; + + m:d | id:i +----------------------+--------------- +2.2994842882726847E-19|1 +9.046030383433917E-20 |2 +1.3813254901492728E-19|3 +7.763447541524763E-20 |4 +1.5430743541977252E-19|5 +1.1302799995492255E-19|6 +5.530629782187966E-20 |7 +5.827842232431528E-20 |8 +5.459386906856704E-20 |9 +; + +averageWithOneValueAndOrderLargeInput +SELECT * FROM (SELECT id, status, bytes_out FROM logs_unsigned_long) PIVOT (AVG(bytes_out + 1) FOR status IN ('OK', 'Error')) ORDER BY id DESC LIMIT 4; + + id:i | 'OK':d | 'Error':d +---------------+---------------------+--------------- +101 |null |null +100 |1.5616395223975498E19|null +99 |3.5244227329937082E17|null +98 |5.4095497308894812E18|null +; + +sumWithInnerAggregateSumOfSquaresRoundLargeInput +SELECT * FROM logs_unsigned_long PIVOT (ROUND(SUM_OF_SQUARES(bytes_out + 1)/1E6, 2) FOR status IN ('Error', 'OK')) LIMIT 3; + + @timestamp:ts | bytes_in:ul | id:i | 'Error':d | 'OK':d +------------------------+-------------------+---------------+---------------+-------------------- +2017-11-10T00:00:22.000Z|0 |80 |null |9.223372036854776E16 +2017-11-10T00:01:04.000Z|9636626466125797351|83 |null |9.223372036854776E16 +2017-11-10T00:01:20.000Z|74330435873664882 |82 |null |9.223372036854776E16 +; + +castWithGroupByLargeInput +SELECT (bytes_in/1E12)::LONG::STRING in_tib FROM logs_unsigned_long WHERE bytes_in - 18E18 > 0 GROUP BY 1; + + in_tib:s +-------------------- +18081123 +18098466 +18317075 +18345361 +18446744 +; diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/unsigned-long.sql-spec b/x-pack/plugin/sql/qa/server/src/main/resources/unsigned-long.sql-spec new file mode 100644 index 0000000000000..6e893af92e445 --- /dev/null +++ b/x-pack/plugin/sql/qa/server/src/main/resources/unsigned-long.sql-spec @@ -0,0 +1,73 @@ +// To mute tests follow example in file: example.sql-spec + +// +// Unsigned long tests +// + +// H2 doesn't support our unsigned_long type, casting to it isn't possible. +// Values larger than Long.MAX_VALUE are returned as BigDecimal. +// EXPM1 and CBRT functions not available. +// RAND takes an int. +// ES's CEIL & FLOOR don't work with unsigned_longs (TODO) + they can return longs, while H2's always returns doubles + +plus +SELECT 18446744073709551614 + 1 AS x; +plusLongMax +SELECT 9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000) AS x; +minus +SELECT 18446744073709551615 - ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000) AS x; +divide +SELECT 18446744073709551615 / ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000) AS x; +multiply +SELECT 18446744073709551615 * 1 AS x; +mod +SELECT 18446744073709551615 % ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000) AS x; + +abs +SELECT ABS(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +// ceil +// SELECT CEIL(9223372036854775808 + RAND(SECOND(CURRENT_TIMESTAMP())) * 10000) AS x; +exp +SELECT EXP(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +// floor +// SELECT FLOOR(9223372036854775808 + RAND(SECOND(CURRENT_TIMESTAMP())) * 10000) AS x; +log +SELECT LOG(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +log10 +SELECT LOG10(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +power +SELECT POWER(4294967295, 2) AS x; +round +SELECT ROUND(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +sign +SELECT SIGN(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +sqrt +SELECT SQRT(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +truncate +SELECT TRUNCATE(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +acos + +// trigonometric +SELECT ACOS(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +asin +SELECT ASIN(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +atan +SELECT ATAN(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +atan2 +SELECT ATAN2(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000), 45) AS x; +cos +SELECT COS(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +cosh +SELECT COSH(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +cot +SELECT COT(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +degrees +SELECT DEGREES(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +radians +SELECT RADIANS(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +sin +SELECT SIN(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +sinh +SELECT SINH(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; +tan +SELECT TAN(9223372036854775808 + ROUND(RAND(SECOND(CURRENT_TIMESTAMP())) * 10000)) AS x; diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/SqlVersion.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/SqlVersion.java index ff74c14797a50..09a931dc7204a 100644 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/SqlVersion.java +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/SqlVersion.java @@ -32,7 +32,7 @@ public class SqlVersion implements Comparable { public static final SqlVersion V_7_7_0 = new SqlVersion(7, 7, 0); public static final SqlVersion V_7_12_0 = new SqlVersion(7, 12, 0); - public static final SqlVersion DATE_NANOS_SUPPORT_VERSION = V_7_12_0; + public static final SqlVersion DATE_NANOS_SUPPORT_VERSION = V_7_12_0; // TODO: move to VersionCompatibilityChecks public SqlVersion(byte major, byte minor, byte revision) { this(toString(major, minor, revision), major, minor, revision); @@ -169,6 +169,7 @@ public static boolean isClientCompatible(SqlVersion server, SqlVersion client) { return hasVersionCompatibility(client) && server.compareTo(client) >= 0 && server.major - client.major <= 1; } + // TODO: move to VersionCompatibilityChecks public static boolean supportsDateNanos(SqlVersion version) { return DATE_NANOS_SUPPORT_VERSION.compareTo(version) <= 0; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java index 63ffd74be4ab0..14a25fc2afb49 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java @@ -44,7 +44,6 @@ import org.elasticsearch.xpack.ql.plan.logical.UnaryPlan; import org.elasticsearch.xpack.ql.plan.logical.UnresolvedRelation; import org.elasticsearch.xpack.ql.rule.RuleExecutor; -import org.elasticsearch.xpack.ql.session.Configuration; import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.ql.type.InvalidMappedField; @@ -59,6 +58,7 @@ import org.elasticsearch.xpack.sql.plan.logical.Pivot; import org.elasticsearch.xpack.sql.plan.logical.SubQueryAlias; import org.elasticsearch.xpack.sql.plan.logical.With; +import org.elasticsearch.xpack.sql.session.SqlConfiguration; import org.elasticsearch.xpack.sql.type.SqlDataTypeConverter; import java.util.ArrayList; @@ -92,13 +92,13 @@ public class Analyzer extends RuleExecutor { * Per-request specific settings needed in some of the functions (timezone, username and clustername), * to which they are attached. */ - private final Configuration configuration; + private final SqlConfiguration configuration; /** * The verifier has the role of checking the analyzed tree for failures and build a list of failures. */ private final Verifier verifier; - public Analyzer(Configuration configuration, FunctionRegistry functionRegistry, IndexResolution results, Verifier verifier) { + public Analyzer(SqlConfiguration configuration, FunctionRegistry functionRegistry, IndexResolution results, Verifier verifier) { this.configuration = configuration; this.functionRegistry = functionRegistry; this.indexResolution = results; @@ -149,7 +149,7 @@ public ExecutionInfo debugAnalyze(LogicalPlan plan) { } public LogicalPlan verify(LogicalPlan plan) { - Collection failures = verifier.verify(plan); + Collection failures = verifier.verify(plan, configuration.version()); if (failures.isEmpty() == false) { throw new VerificationException(failures); } @@ -241,8 +241,7 @@ else if (DataTypes.isUnsupported(fa.dataType())) { + fa.name() + "] with unsupported type [" + unsupportedField.getOriginalType() - + "] " - + "in hierarchy (field [" + + "] in hierarchy (field [" + unsupportedField.getInherited() + "])" ); @@ -340,7 +339,7 @@ protected LogicalPlan rule(UnresolvedRelation plan) { } } - private static class ResolveRefs extends BaseAnalyzerRule { + private class ResolveRefs extends BaseAnalyzerRule { @Override protected LogicalPlan doRule(LogicalPlan plan) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java index 544b53c041799..720e17c54ecf5 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java @@ -6,6 +6,7 @@ */ package org.elasticsearch.xpack.sql.analysis.analyzer; +import org.elasticsearch.Version; import org.elasticsearch.core.Tuple; import org.elasticsearch.xpack.ql.capabilities.Unresolvable; import org.elasticsearch.xpack.ql.common.Failure; @@ -57,6 +58,7 @@ import org.elasticsearch.xpack.sql.plan.logical.LocalRelation; import org.elasticsearch.xpack.sql.plan.logical.Pivot; import org.elasticsearch.xpack.sql.plan.logical.command.Command; +import org.elasticsearch.xpack.sql.proto.SqlVersion; import org.elasticsearch.xpack.sql.stats.FeatureMetric; import org.elasticsearch.xpack.sql.stats.Metrics; import org.elasticsearch.xpack.sql.type.SqlDataTypes; @@ -75,7 +77,10 @@ import static java.util.stream.Collectors.toMap; import static org.elasticsearch.xpack.ql.analyzer.VerifierChecks.checkFilterConditionType; import static org.elasticsearch.xpack.ql.common.Failure.fail; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.isTypeSupportedInVersion; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.versionIntroducingType; import static org.elasticsearch.xpack.ql.type.DataTypes.BINARY; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.ql.util.CollectionUtils.combine; import static org.elasticsearch.xpack.sql.stats.FeatureMetric.COMMAND; import static org.elasticsearch.xpack.sql.stats.FeatureMetric.GROUPBY; @@ -98,12 +103,12 @@ public Verifier(Metrics metrics) { this.metrics = metrics; } - public Map, String> verifyFailures(LogicalPlan plan) { - Collection failures = verify(plan); + public Map, String> verifyFailures(LogicalPlan plan, SqlVersion version) { + Collection failures = verify(plan, version); return failures.stream().collect(toMap(Failure::node, Failure::message)); } - Collection verify(LogicalPlan plan) { + Collection verify(LogicalPlan plan, SqlVersion version) { Set failures = new LinkedHashSet<>(); // start bottom-up @@ -235,6 +240,8 @@ Collection verify(LogicalPlan plan) { failures.addAll(localFailures); }); + + checkClientSupportsDataTypes(plan, failures, version); } // gather metrics @@ -463,8 +470,9 @@ private static boolean checkGroupByHavingHasOnlyAggs( unsupported.add(e); return true; } else if (e instanceof Min || e instanceof Max) { - if (DataTypes.isString(((AggregateFunction) e).field().dataType())) { - // Min & Max on a Keyword field will be translated to First & Last respectively + DataType aggType = ((AggregateFunction) e).field().dataType(); + if (DataTypes.isString(aggType) || aggType == UNSIGNED_LONG) { + // Min & Max on a Keyword or unsigned_long field will be translated to First & Last respectively unsupported.add(e); return true; } @@ -995,6 +1003,28 @@ private static void checkCastOnInexact(LogicalPlan p, Set localFailures })); } + private static void checkClientSupportsDataTypes(LogicalPlan p, Set localFailures, SqlVersion version) { + Version ver = Version.fromId(version.id); + p.output().forEach(e -> { + if (e.resolved() && isTypeSupportedInVersion(e.dataType(), ver) == false) { + localFailures.add( + fail( + e, + "Cannot use field [" + + e.name() + + "] with type [" + + e.dataType() + + "] unsupported in version [" + + version + + "], upgrade required (to version [" + + versionIntroducingType(e.dataType()) + + "] or higher)" + ) + ); + } + }); + } + // check that any binary field used in WHERE, GROUP BY, HAVING or ORDER BY has doc_values, for ES to allow querying it private static void checkBinaryHasDocValues(LogicalPlan plan, Set localFailures) { List> fields = new ArrayList<>(); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/Querier.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/Querier.java index d55ec64adbc2d..dbdb23b30d914 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/Querier.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/Querier.java @@ -83,7 +83,7 @@ import static java.util.Collections.singletonList; import static org.elasticsearch.action.ActionListener.wrap; -import static org.elasticsearch.xpack.ql.execution.search.QlSourceBuilder.INTRODUCING_MISSING_ORDER_IN_COMPOSITE_AGGS_VERSION; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.INTRODUCING_UNSIGNED_LONG; // TODO: add retry/back-off public class Querier { @@ -144,7 +144,7 @@ public void query(List output, QueryContainer query, String index, Ac public static SearchRequest prepareRequest(SearchSourceBuilder source, TimeValue timeout, boolean includeFrozen, String... indices) { source.timeout(timeout); - SearchRequest searchRequest = new SearchRequest(INTRODUCING_MISSING_ORDER_IN_COMPOSITE_AGGS_VERSION); + SearchRequest searchRequest = new SearchRequest(INTRODUCING_UNSIGNED_LONG); searchRequest.indices(indices); searchRequest.source(source); searchRequest.allowPartialSearchResults(false); @@ -468,7 +468,7 @@ protected List initBucketExtractors(SearchResponse response) { private BucketExtractor createExtractor(FieldExtraction ref, BucketExtractor totalCount) { if (ref instanceof GroupByRef r) { - return new CompositeKeyExtractor(r.key(), r.property(), cfg.zoneId(), r.isDateTimeBased()); + return new CompositeKeyExtractor(r.key(), r.property(), cfg.zoneId(), r.dataType()); } if (ref instanceof MetricAggRef r) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractor.java index 41f6345ccf34b..010d0fafade16 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractor.java @@ -6,13 +6,16 @@ */ package org.elasticsearch.xpack.sql.execution.search.extractor; +import org.elasticsearch.Version; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket; import org.elasticsearch.xpack.ql.execution.search.extractor.BucketExtractor; +import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.common.io.SqlStreamInput; import org.elasticsearch.xpack.sql.querydsl.container.GroupByRef.Property; +import org.elasticsearch.xpack.sql.type.SqlDataTypes; import org.elasticsearch.xpack.sql.util.DateUtils; import java.io.IOException; @@ -20,6 +23,13 @@ import java.util.Map; import java.util.Objects; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.INTRODUCING_UNSIGNED_LONG; +import static org.elasticsearch.xpack.ql.type.DataTypeConverter.toUnsignedLong; +import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; +import static org.elasticsearch.xpack.ql.type.DataTypes.NULL; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; +import static org.elasticsearch.xpack.sql.type.SqlDataTypes.isDateBased; + public class CompositeKeyExtractor implements BucketExtractor { /** @@ -30,22 +40,27 @@ public class CompositeKeyExtractor implements BucketExtractor { private final String key; private final Property property; private final ZoneId zoneId; - private final boolean isDateTimeBased; + private final DataType dataType; /** * Constructs a new CompositeKeyExtractor instance. */ - public CompositeKeyExtractor(String key, Property property, ZoneId zoneId, boolean isDateTimeBased) { + public CompositeKeyExtractor(String key, Property property, ZoneId zoneId, DataType dataType) { this.key = key; this.property = property; this.zoneId = zoneId; - this.isDateTimeBased = isDateTimeBased; + this.dataType = dataType; } CompositeKeyExtractor(StreamInput in) throws IOException { key = in.readString(); property = in.readEnum(Property.class); - isDateTimeBased = in.readBoolean(); + if (in.getVersion().onOrAfter(Version.fromId(INTRODUCING_UNSIGNED_LONG.id))) { + dataType = SqlDataTypes.fromTypeName(in.readString()); + } else { + // for pre-UNSIGNED_LONG versions, the only relevant fact about the dataType was if this isDateBased() or not. + dataType = in.readBoolean() ? DATETIME : NULL; + } zoneId = SqlStreamInput.asSqlStream(in).zoneId(); } @@ -54,7 +69,11 @@ public CompositeKeyExtractor(String key, Property property, ZoneId zoneId, boole public void writeTo(StreamOutput out) throws IOException { out.writeString(key); out.writeEnum(property); - out.writeBoolean(isDateTimeBased); + if (out.getVersion().onOrAfter(Version.fromId(INTRODUCING_UNSIGNED_LONG.id))) { + out.writeString(dataType.typeName()); + } else { + out.writeBoolean(isDateBased(dataType)); + } } String key() { @@ -69,8 +88,8 @@ ZoneId zoneId() { return zoneId; } - public boolean isDateTimeBased() { - return isDateTimeBased; + public DataType dataType() { + return dataType; } @Override @@ -92,13 +111,21 @@ public Object extract(Bucket bucket) { Object object = ((Map) m).get(key); - if (isDateTimeBased) { - if (object == null) { - return object; - } else if (object instanceof Long) { - object = DateUtils.asDateTimeWithMillis(((Long) object).longValue(), zoneId); - } else { - throw new SqlIllegalArgumentException("Invalid date key returned: {}", object); + if (object != null) { + if (isDateBased(dataType)) { + if (object instanceof Long l) { + object = DateUtils.asDateTimeWithMillis(l, zoneId); + } else { + throw new SqlIllegalArgumentException("Invalid date key returned: {}", object); + } + } else if (dataType == UNSIGNED_LONG) { + // For integral types we coerce the bucket type to long in composite aggs (unsigned_long is not an available choice). So + // when getting back a long value, this needs to be type- and value-converted to an UNSIGNED_LONG + if (object instanceof Number number) { + object = toUnsignedLong(number); + } else { + throw new SqlIllegalArgumentException("Invalid unsigned_long key returned: {}", object); + } } } @@ -107,7 +134,7 @@ public Object extract(Bucket bucket) { @Override public int hashCode() { - return Objects.hash(key, property, zoneId, isDateTimeBased); + return Objects.hash(key, property, zoneId, dataType); } @Override @@ -124,7 +151,7 @@ public boolean equals(Object obj) { return Objects.equals(key, other.key) && Objects.equals(property, other.property) && Objects.equals(zoneId, other.zoneId) - && Objects.equals(isDateTimeBased, other.isDateTimeBased); + && Objects.equals(dataType, other.dataType); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java index 989aa2c9ded85..54187aa0ffde2 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractor.java @@ -26,6 +26,8 @@ import java.util.Map; import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; +import static org.elasticsearch.xpack.sql.type.SqlDataTypeConverter.convert; import static org.elasticsearch.xpack.sql.type.SqlDataTypes.GEO_POINT; import static org.elasticsearch.xpack.sql.type.SqlDataTypes.GEO_SHAPE; import static org.elasticsearch.xpack.sql.type.SqlDataTypes.SHAPE; @@ -126,6 +128,11 @@ protected Object unwrapCustomValue(Object values) { return DateUtils.asDateTimeWithNanos(values.toString()).withZoneSameInstant(zoneId()); } } + if (dataType == UNSIGNED_LONG) { + // Unsigned longs can be returned either as such (for values exceeding long range) or as longs. Value conversion is needed + // since its later processing will be type dependent. (ex.: negation of UL is only "safe" for 0 values) + return convert(values, UNSIGNED_LONG); + } return null; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/TopHitsAggExtractor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/TopHitsAggExtractor.java index fe9125fb2f581..78976ea7e83c0 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/TopHitsAggExtractor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/extractor/TopHitsAggExtractor.java @@ -21,7 +21,9 @@ import java.time.ZoneId; import java.util.Objects; +import static org.elasticsearch.xpack.ql.type.DataTypeConverter.toUnsignedLong; import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.sql.type.SqlDataTypes.DATE; public class TopHitsAggExtractor implements BucketExtractor { @@ -83,6 +85,16 @@ public Object extract(Bucket bucket) { return DateUtils.asDateTimeWithNanos(value.toString()).withZoneSameInstant(zoneId()); } else if (SqlDataTypes.isTimeBased(fieldDataType)) { return DateUtils.asTimeOnly(Long.parseLong(value.toString()), zoneId); + } else if (fieldDataType == UNSIGNED_LONG) { + if (value == null) { + return null; + } else if (value instanceof Number number) { + // values can be returned either as unsigned_longs or longs (if range allows), which can lead to cast exceptions + // when sorting rows locally -> upcast to BigInteger + return toUnsignedLong(number); + } else { + throw new SqlIllegalArgumentException("Invalid unsigned_long key returned: {}", value); + } } else { return value; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Sum.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Sum.java index 19e5c00dc79b5..4b6d004e7558f 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Sum.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Sum.java @@ -16,6 +16,7 @@ import static org.elasticsearch.xpack.ql.type.DataTypes.DOUBLE; import static org.elasticsearch.xpack.ql.type.DataTypes.LONG; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; /** * Sum all values of a field in matching documents. @@ -38,7 +39,8 @@ public Sum replaceChildren(List newChildren) { @Override public DataType dataType() { - return field().dataType().isInteger() ? LONG : DOUBLE; + DataType dt = field().dataType(); + return dt.isInteger() == false || dt == UNSIGNED_LONG ? DOUBLE : LONG; } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathProcessor.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathProcessor.java index 9b45b18ea7236..a6306193d18b6 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathProcessor.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathProcessor.java @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import java.io.IOException; +import java.math.BigInteger; import java.util.Random; import java.util.function.DoubleFunction; import java.util.function.Function; @@ -30,6 +31,9 @@ public enum MathOperation { if (l instanceof Float) { return Math.abs(((Float) l).floatValue()); } + if (l instanceof BigInteger) { + return ((BigInteger) l).abs(); + } // fallback to integer long lo = ((Number) l).longValue(); @@ -81,6 +85,9 @@ public enum MathOperation { if (l instanceof Float) { return (int) Math.signum((Float) l); } + if (l instanceof BigInteger) { + return ((BigInteger) l).signum(); + } return Long.signum(((Number) l).longValue()); }), diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java index 90a060009c0de..81854eee8cda3 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/optimizer/Optimizer.java @@ -53,6 +53,7 @@ import org.elasticsearch.xpack.ql.rule.Rule; import org.elasticsearch.xpack.ql.rule.RuleExecutor; import org.elasticsearch.xpack.ql.tree.Source; +import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.ql.util.Holder; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; @@ -107,6 +108,7 @@ import static org.elasticsearch.xpack.ql.expression.Expressions.equalsAsAttribute; import static org.elasticsearch.xpack.ql.optimizer.OptimizerRules.BinaryComparisonSimplification; import static org.elasticsearch.xpack.ql.optimizer.OptimizerRules.PushDownAndCombineFilters; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.ql.util.CollectionUtils.combine; public class Optimizer extends RuleExecutor { @@ -1149,12 +1151,15 @@ protected LogicalPlan rule(LogicalPlan plan) { Map maxs = new HashMap<>(); return plan.transformExpressionsDown(NumericAggregate.class, e -> { if (e instanceof Min min) { - if (DataTypes.isString(min.field().dataType())) { + DataType minType = min.field().dataType(); + // upper range unsigned longs can't be represented exactly on doubles (returned by stats agg) + if (DataTypes.isString(minType) || minType == UNSIGNED_LONG) { return mins.computeIfAbsent(min.field(), k -> new First(min.source(), k, null)); } } if (e instanceof Max max) { - if (DataTypes.isString(max.field().dataType())) { + DataType maxType = max.field().dataType(); + if (DataTypes.isString(maxType) || maxType == UNSIGNED_LONG) { return maxs.computeIfAbsent(max.field(), k -> new Last(max.source(), k, null)); } } 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 030055bad6da8..848939662a4f0 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 @@ -714,22 +714,12 @@ public Literal visitDecimalLiteral(DecimalLiteralContext ctx) { @Override public Literal visitIntegerLiteral(IntegerLiteralContext ctx) { Tuple tuple = withMinus(ctx); - - long value; try { - value = Long.valueOf(StringUtils.parseLong(tuple.v2())); + Number value = StringUtils.parseIntegral(tuple.v2()); + return new Literal(tuple.v1(), value, DataTypes.fromJava(value)); } catch (QlIllegalArgumentException siae) { throw new ParsingException(tuple.v1(), siae.getMessage()); } - - Object val = Long.valueOf(value); - DataType type = DataTypes.LONG; - // try to downsize to int if possible (since that's the most common type) - if ((int) value == value) { - type = DataTypes.INTEGER; - val = Integer.valueOf((int) value); - } - return new Literal(tuple.v1(), val, type); } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Explain.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Explain.java index 1fe9e121bcc17..e82e9070f1cdb 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Explain.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/Explain.java @@ -140,7 +140,7 @@ public void execute(SqlSession session, ActionListener listener) { // check errors manually to see how far the plans work out else { // no analysis failure, can move on - if (session.verifier().verifyFailures(analyzedPlan).isEmpty()) { + if (session.verifier().verifyFailures(analyzedPlan, session.configuration().version()).isEmpty()) { session.optimizedPlan(analyzedPlan, wrap(optimizedPlan -> { if (type == Type.OPTIMIZED) { listener.onResponse(Page.last(Rows.singleton(output(), formatPlan(format, optimizedPlan)))); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java index 9833c747a1933..acee318c27077 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumns.java @@ -6,10 +6,12 @@ */ package org.elasticsearch.xpack.sql.plan.logical.command; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.xpack.ql.expression.Attribute; import org.elasticsearch.xpack.ql.expression.FieldAttribute; import org.elasticsearch.xpack.ql.expression.predicate.regex.LikePattern; +import org.elasticsearch.xpack.ql.index.IndexCompatibility; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataType; @@ -82,13 +84,14 @@ public void execute(SqlSession session, ActionListener listener) { List> rows = emptyList(); if (indexResult.isValid()) { rows = new ArrayList<>(); - fillInRows(indexResult.get().mapping(), null, rows); + Version version = Version.fromId(session.configuration().version().id); + fillInRows(IndexCompatibility.compatible(indexResult, version).get().mapping(), null, rows); } listener.onResponse(of(session, rows)); }, listener::onFailure)); } - private void fillInRows(Map mapping, String prefix, List> rows) { + static void fillInRows(Map mapping, String prefix, List> rows) { for (Entry e : mapping.entrySet()) { EsField field = e.getValue(); DataType dt = field.getDataType(); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java index 14992e9e8f926..e18f03d503767 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumns.java @@ -7,11 +7,13 @@ package org.elasticsearch.xpack.sql.plan.logical.command.sys; import org.apache.lucene.util.Counter; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.Strings; import org.elasticsearch.xpack.ql.expression.Attribute; import org.elasticsearch.xpack.ql.expression.predicate.regex.LikePattern; import org.elasticsearch.xpack.ql.index.EsIndex; +import org.elasticsearch.xpack.ql.index.IndexCompatibility; import org.elasticsearch.xpack.ql.tree.NodeInfo; import org.elasticsearch.xpack.ql.tree.Source; import org.elasticsearch.xpack.ql.type.DataType; @@ -153,12 +155,14 @@ public void execute(SqlSession session, ActionListener listener) { tableCat = cluster; } + Version version = Version.fromId(session.configuration().version().id); // special case for '%' (translated to *) if ("*".equals(idx)) { session.indexResolver() .resolveAsSeparateMappings(indexPattern, regex, includeFrozen, emptyMap(), ActionListener.wrap(esIndices -> { List> rows = new ArrayList<>(); for (EsIndex esIndex : esIndices) { + IndexCompatibility.compatible(esIndex, version); fillInRows(tableCat, esIndex.name(), esIndex.mapping(), null, rows, columnMatcher, mode); } listener.onResponse(ListCursor.of(Rows.schema(output), rows, session.configuration().pageSize())); @@ -170,8 +174,15 @@ public void execute(SqlSession session, ActionListener listener) { List> rows = new ArrayList<>(); // populate the data only when a target is found if (r.isValid()) { - EsIndex esIndex = r.get(); - fillInRows(tableCat, indexName, esIndex.mapping(), null, rows, columnMatcher, mode); + fillInRows( + tableCat, + indexName, + IndexCompatibility.compatible(r, version).get().mapping(), + null, + rows, + columnMatcher, + mode + ); } listener.onResponse(ListCursor.of(Rows.schema(output), rows, session.configuration().pageSize())); }, listener::onFailure)); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypes.java index 1adf275767aaa..66efd1a4ee879 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypes.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypes.java @@ -6,6 +6,7 @@ */ package org.elasticsearch.xpack.sql.plan.logical.command.sys; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.xpack.ql.expression.Attribute; import org.elasticsearch.xpack.ql.tree.NodeInfo; @@ -23,6 +24,7 @@ import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.isTypeSupportedInVersion; import static org.elasticsearch.xpack.ql.type.DataTypes.BOOLEAN; import static org.elasticsearch.xpack.ql.type.DataTypes.INTEGER; import static org.elasticsearch.xpack.ql.type.DataTypes.SHORT; @@ -83,7 +85,9 @@ public List output() { @Override public final void execute(SqlSession session, ActionListener listener) { - Stream values = SqlDataTypes.types().stream(); + Stream values = SqlDataTypes.types() + .stream() + .filter(t -> isTypeSupportedInVersion(t, Version.fromId(session.configuration().version().id))); if (type.intValue() != 0) { values = values.filter(t -> type.equals(sqlType(t).getVendorTypeNumber())); } @@ -106,7 +110,7 @@ public final void execute(SqlSession session, ActionListener listener) { isString(t), // everything is searchable, DatabaseMetaData.typeSearchable, - // only numerics are signed + // only numerics (sans UNSIGNED_LONG) are signed isSigned(t) == false, // no fixed precision scale SQL_FALSE Boolean.FALSE, diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java index b4ddb82a4a155..fc2d6d22ccf86 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java @@ -40,6 +40,7 @@ import org.elasticsearch.xpack.ql.querydsl.query.Query; import org.elasticsearch.xpack.ql.rule.Rule; import org.elasticsearch.xpack.ql.rule.RuleExecutor; +import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.expression.function.Score; @@ -95,6 +96,7 @@ import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicReference; +import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; import static org.elasticsearch.xpack.ql.util.CollectionUtils.combine; import static org.elasticsearch.xpack.sql.expression.function.grouping.Histogram.DAY_INTERVAL; import static org.elasticsearch.xpack.sql.expression.function.grouping.Histogram.MONTH_INTERVAL; @@ -554,7 +556,7 @@ else if (target instanceof Function) { if (matchingGroup != null) { if (exp instanceof Attribute || exp instanceof ScalarFunction || exp instanceof GroupingFunction) { Processor action = null; - boolean isDateBased = isDateBased(exp.dataType()); + DataType dataType = exp.dataType(); /* * special handling of dates since aggs return the typed Date object which needs * extraction instead of handling this in the scroller, the folder handles this @@ -562,14 +564,9 @@ else if (target instanceof Function) { */ if (exp instanceof DateTimeHistogramFunction) { action = ((UnaryPipe) p).action(); - isDateBased = true; + dataType = DATETIME; } - return new AggPathInput( - exp.source(), - exp, - new GroupByRef(matchingGroup.id(), null, isDateBased), - action - ); + return new AggPathInput(exp.source(), exp, new GroupByRef(matchingGroup.id(), null, dataType), action); } } // or found an aggregate expression (which has to work on an attribute used for grouping) @@ -607,19 +604,11 @@ else if (target instanceof Function) { // attributes can only refer to declared groups if (target instanceof Attribute) { Check.notNull(matchingGroup, "Cannot find group [{}]", Expressions.name(target)); - queryC = queryC.addColumn( - new GroupByRef(matchingGroup.id(), null, isDateBased(target.dataType())), - id, - ne.toAttribute() - ); + queryC = queryC.addColumn(new GroupByRef(matchingGroup.id(), null, target.dataType()), id, ne.toAttribute()); } // handle histogram else if (target instanceof GroupingFunction) { - queryC = queryC.addColumn( - new GroupByRef(matchingGroup.id(), null, isDateBased(target.dataType())), - id, - ne.toAttribute() - ); + queryC = queryC.addColumn(new GroupByRef(matchingGroup.id(), null, target.dataType()), id, ne.toAttribute()); } // handle literal else if (target.foldable()) { @@ -650,11 +639,7 @@ else if (target.foldable()) { matchingGroup = groupingContext.groupFor(target); Check.notNull(matchingGroup, "Cannot find group [{}]", Expressions.name(ne)); - queryC = queryC.addColumn( - new GroupByRef(matchingGroup.id(), null, isDateBased(ne.dataType())), - id, - ne.toAttribute() - ); + queryC = queryC.addColumn(new GroupByRef(matchingGroup.id(), null, ne.dataType()), id, ne.toAttribute()); } // fallback else { @@ -667,7 +652,7 @@ else if (target.foldable()) { if (a.aggregates().stream().allMatch(e -> e.anyMatch(Expression::foldable))) { for (Expression grouping : a.groupings()) { GroupByKey matchingGroup = groupingContext.groupFor(grouping); - queryC = queryC.addColumn(new GroupByRef(matchingGroup.id(), null, false), id, null); + queryC = queryC.addColumn(new GroupByRef(matchingGroup.id(), null, grouping.dataType()), id, null); } } return new EsQueryExec(exec.source(), exec.index(), a.output(), queryC); @@ -692,7 +677,7 @@ private static Tuple addAggFunction( // if the count points to the total track hits, enable accurate count retrieval queryC = queryC.withTrackHits(); } else { - ref = new GroupByRef(groupingAgg.id(), Property.COUNT, false); + ref = new GroupByRef(groupingAgg.id(), Property.COUNT, c.dataType()); } Map pseudoFunctions = new LinkedHashMap<>(queryC.pseudoFunctions()); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/GroupByRef.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/GroupByRef.java index 3ce1d6c5cc694..c22dd6a13e1ea 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/GroupByRef.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/container/GroupByRef.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.querydsl.container; import org.elasticsearch.xpack.ql.execution.search.AggRef; +import org.elasticsearch.xpack.ql.type.DataType; /** * Reference to a GROUP BY agg (typically this gets translated to a composite key). @@ -20,12 +21,12 @@ public enum Property { private final String key; private final Property property; - private final boolean isDateTimeBased; + private final DataType dataType; - public GroupByRef(String key, Property property, boolean isDateTimeBased) { + public GroupByRef(String key, Property property, DataType dataType) { this.key = key; this.property = property == null ? Property.VALUE : property; - this.isDateTimeBased = isDateTimeBased; + this.dataType = dataType; } public String key() { @@ -36,8 +37,8 @@ public Property property() { return property; } - public boolean isDateTimeBased() { - return isDateTimeBased; + public DataType dataType() { + return dataType; } @Override diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java index 24494cf3251e9..85b411344989d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/SqlSession.java @@ -6,11 +6,13 @@ */ package org.elasticsearch.xpack.sql.session; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.internal.Client; import org.elasticsearch.client.internal.ParentTaskAssigningClient; import org.elasticsearch.tasks.TaskCancelledException; import org.elasticsearch.xpack.ql.expression.function.FunctionRegistry; +import org.elasticsearch.xpack.ql.index.IndexCompatibility; import org.elasticsearch.xpack.ql.index.IndexResolution; import org.elasticsearch.xpack.ql.index.IndexResolver; import org.elasticsearch.xpack.ql.index.MappingException; @@ -113,8 +115,13 @@ public void analyzedPlan(LogicalPlan parsed, boolean verify, ActionListener { - Analyzer analyzer = new Analyzer(configuration, functionRegistry, c, verifier); + preAnalyze(parsed, r -> { + Analyzer analyzer = new Analyzer( + configuration, + functionRegistry, + IndexCompatibility.compatible(r, Version.fromId(configuration.version().id)), + verifier + ); return analyzer.analyze(parsed, verify); }, listener); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/SqlDataTypeConverter.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/SqlDataTypeConverter.java index 00c67c0e06a76..970e3dad5bc62 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/SqlDataTypeConverter.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/SqlDataTypeConverter.java @@ -9,7 +9,6 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.xpack.ql.QlIllegalArgumentException; import org.elasticsearch.xpack.ql.type.Converter; import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypeConverter; @@ -21,7 +20,6 @@ import java.io.IOException; import java.time.OffsetTime; import java.time.ZonedDateTime; -import java.time.format.DateTimeParseException; import java.util.function.Function; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.BOOL_TO_INT; @@ -32,10 +30,12 @@ import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.DATETIME_TO_INT; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.DATETIME_TO_LONG; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.DATETIME_TO_SHORT; +import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.DATETIME_TO_UNSIGNED_LONG; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.IDENTITY; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.INTEGER_TO_LONG; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.RATIONAL_TO_LONG; import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.TO_NULL; +import static org.elasticsearch.xpack.ql.type.DataTypeConverter.DefaultConverter.fromString; import static org.elasticsearch.xpack.ql.type.DataTypes.BOOLEAN; import static org.elasticsearch.xpack.ql.type.DataTypes.BYTE; import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; @@ -47,6 +47,7 @@ import static org.elasticsearch.xpack.ql.type.DataTypes.NULL; import static org.elasticsearch.xpack.ql.type.DataTypes.SHORT; import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.ql.type.DataTypes.isDateTime; import static org.elasticsearch.xpack.ql.type.DataTypes.isPrimitive; import static org.elasticsearch.xpack.ql.type.DataTypes.isString; @@ -82,17 +83,11 @@ public static DataType commonType(DataType left, DataType right) { return DATETIME; } if (left == TIME) { - if (right == DATE) { - return DATETIME; - } if (isInterval(right)) { return left; } } if (right == TIME) { - if (left == DATE) { - return DATETIME; - } if (isInterval(left)) { return right; } @@ -165,6 +160,9 @@ public static Converter converterFor(DataType from, DataType to) { if (to == KEYWORD || to == TEXT) { return conversionToString(from); } + if (to == UNSIGNED_LONG) { + return conversionToUnsignedLong(from); + } if (to == LONG) { return conversionToLong(from); } @@ -204,6 +202,16 @@ private static Converter conversionToString(DataType from) { return null; } + private static Converter conversionToUnsignedLong(DataType from) { + if (from == DATE) { + return SqlConverter.DATE_TO_UNSIGNED_LONG; + } + if (from == TIME) { + return SqlConverter.TIME_TO_UNSIGNED_LONG; + } + return null; + } + private static Converter conversionToLong(DataType from) { if (from == DATE) { return SqlConverter.DATE_TO_LONG; @@ -351,6 +359,9 @@ public enum SqlConverter implements Converter { DATE_TO_STRING(o -> DateUtils.toDateString((ZonedDateTime) o)), TIME_TO_STRING(o -> DateUtils.toTimeString((OffsetTime) o)), + DATE_TO_UNSIGNED_LONG(delegate(DATETIME_TO_UNSIGNED_LONG)), + TIME_TO_UNSIGNED_LONG(fromTime(DataTypeConverter::safeToUnsignedLong)), + DATE_TO_LONG(delegate(DATETIME_TO_LONG)), TIME_TO_LONG(fromTime(value -> value)), @@ -419,18 +430,6 @@ private static Function delegate(Converter converter) { return converter::convert; } - private static Function fromString(Function converter, String to) { - return (Object value) -> { - try { - return converter.apply(value.toString()); - } catch (NumberFormatException e) { - throw new QlIllegalArgumentException(e, "cannot cast [{}] to [{}]", value, to); - } catch (DateTimeParseException | IllegalArgumentException e) { - throw new QlIllegalArgumentException(e, "cannot cast [{}] to [{}]: {}", value, to, e.getMessage()); - } - }; - } - @Override public Object convert(Object l) { if (l == null) { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/SqlDataTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/SqlDataTypes.java index cfcc5d82445c4..d211b09289b35 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/SqlDataTypes.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/SqlDataTypes.java @@ -46,6 +46,7 @@ import static org.elasticsearch.xpack.ql.type.DataTypes.SCALED_FLOAT; import static org.elasticsearch.xpack.ql.type.DataTypes.SHORT; import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.ql.type.DataTypes.UNSUPPORTED; import static org.elasticsearch.xpack.ql.type.DataTypes.isDateTime; import static org.elasticsearch.xpack.ql.util.CollectionUtils.mapSize; @@ -98,6 +99,7 @@ public class SqlDataTypes { ODBC_TO_ES.put("SQL_SMALLINT", SHORT); ODBC_TO_ES.put("SQL_INTEGER", INTEGER); ODBC_TO_ES.put("SQL_BIGINT", LONG); + ODBC_TO_ES.put("SQL_UBIGINT", UNSIGNED_LONG); ODBC_TO_ES.put("SQL_REAL", FLOAT); ODBC_TO_ES.put("SQL_FLOAT", DOUBLE); ODBC_TO_ES.put("SQL_DOUBLE", DOUBLE); @@ -334,6 +336,9 @@ public static SQLType sqlType(DataType dataType) { if (dataType == LONG) { return JDBCType.BIGINT; } + if (dataType == UNSIGNED_LONG) { + return JDBCType.NUMERIC; + } if (dataType == DOUBLE) { return JDBCType.DOUBLE; } @@ -457,6 +462,9 @@ public static int defaultPrecision(DataType dataType) { if (dataType == LONG) { return 19; } + if (dataType == UNSIGNED_LONG) { + return 20; + } if (dataType == DOUBLE) { return 15; } @@ -574,7 +582,7 @@ public static int displaySize(DataType dataType) { if (dataType == INTEGER) { return 11; } - if (dataType == LONG) { + if (dataType == LONG || dataType == UNSIGNED_LONG) { return 20; } if (dataType == DOUBLE) { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/FieldAttributeTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/FieldAttributeTests.java index 96f98b1a80417..135b20c2589e1 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/FieldAttributeTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/FieldAttributeTests.java @@ -6,7 +6,10 @@ */ package org.elasticsearch.xpack.sql.analysis.analyzer; +import org.elasticsearch.Version; +import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xcontent.json.JsonXContent; import org.elasticsearch.xpack.ql.QlIllegalArgumentException; import org.elasticsearch.xpack.ql.expression.Alias; import org.elasticsearch.xpack.ql.expression.Attribute; @@ -16,15 +19,21 @@ import org.elasticsearch.xpack.ql.expression.NamedExpression; import org.elasticsearch.xpack.ql.expression.function.FunctionRegistry; import org.elasticsearch.xpack.ql.index.EsIndex; +import org.elasticsearch.xpack.ql.index.IndexCompatibility; import org.elasticsearch.xpack.ql.index.IndexResolution; import org.elasticsearch.xpack.ql.plan.logical.Aggregate; import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.ql.plan.logical.Project; +import org.elasticsearch.xpack.ql.type.DataType; +import org.elasticsearch.xpack.ql.type.DefaultDataTypeRegistry; import org.elasticsearch.xpack.ql.type.EsField; +import org.elasticsearch.xpack.ql.type.Types; import org.elasticsearch.xpack.ql.type.TypesTests; import org.elasticsearch.xpack.sql.SqlTestUtils; import org.elasticsearch.xpack.sql.expression.function.SqlFunctionRegistry; import org.elasticsearch.xpack.sql.parser.SqlParser; +import org.elasticsearch.xpack.sql.proto.SqlVersion; +import org.elasticsearch.xpack.sql.session.SqlConfiguration; import org.elasticsearch.xpack.sql.stats.Metrics; import java.util.Collections; @@ -32,12 +41,16 @@ import java.util.Map; import java.util.stream.Collectors; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.INTRODUCING_UNSIGNED_LONG; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.isTypeSupportedInVersion; import static org.elasticsearch.xpack.ql.type.DataTypes.BOOLEAN; import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD; import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.sql.types.SqlTypesTests.loadMapping; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasSize; @@ -169,7 +182,7 @@ public void testDottedFieldPathTypo() { public void testStarExpansionExcludesObjectAndUnsupportedTypes() { LogicalPlan plan = plan("SELECT * FROM test"); List list = ((Project) plan).projections(); - assertThat(list, hasSize(12)); + assertThat(list, hasSize(13)); List names = Expressions.names(list); assertThat(names, not(hasItem("some"))); assertThat(names, not(hasItem("some.dotted"))); @@ -295,11 +308,139 @@ public void testGroupByAmbiguity() { assertEquals(expected, ex.getMessage()); } + public void testUnsignedLongVersionCompatibility() { + String query = "SELECT unsigned_long FROM test"; + String queryWithLiteral = "SELECT 18446744073709551615 AS unsigned_long"; + String queryWithCastLiteral = "SELECT '18446744073709551615'::unsigned_long AS unsigned_long"; + String queryWithAlias = "SELECT unsigned_long AS unsigned_long FROM test"; + String queryWithArithmetic = "SELECT unsigned_long + 1 AS unsigned_long FROM test"; + String queryWithCast = "SELECT long + 1::unsigned_long AS unsigned_long FROM test"; + + Version preUnsignedLong = Version.fromId(INTRODUCING_UNSIGNED_LONG.id - SqlVersion.MINOR_MULTIPLIER); + Version postUnsignedLong = Version.fromId(INTRODUCING_UNSIGNED_LONG.id + SqlVersion.MINOR_MULTIPLIER); + SqlConfiguration sqlConfig = SqlTestUtils.randomConfiguration(SqlVersion.fromId(preUnsignedLong.id)); + + for (String sql : List.of(query, queryWithLiteral, queryWithCastLiteral, queryWithAlias, queryWithArithmetic, queryWithCast)) { + analyzer = new Analyzer( + sqlConfig, + functionRegistry, + loadCompatibleIndexResolution("mapping-numeric.json", preUnsignedLong), + new Verifier(new Metrics()) + ); + VerificationException ex = expectThrows(VerificationException.class, () -> plan(sql)); + assertThat(ex.getMessage(), containsString("Found 1 problem\nline 1:8: Cannot use field [unsigned_long]")); + + for (Version v : List.of(INTRODUCING_UNSIGNED_LONG, postUnsignedLong)) { + analyzer = new Analyzer( + SqlTestUtils.randomConfiguration(SqlVersion.fromId(v.id)), + functionRegistry, + loadCompatibleIndexResolution("mapping-numeric.json", v), + verifier + ); + LogicalPlan plan = plan(sql); + assertThat(plan, instanceOf(Project.class)); + Project p = (Project) plan; + List projections = p.projections(); + assertThat(projections, hasSize(1)); + Attribute attribute = projections.get(0).toAttribute(); + assertThat(attribute.dataType(), is(UNSIGNED_LONG)); + assertThat(attribute.name(), is("unsigned_long")); + } + } + } + + public void testNonProjectedUnsignedLongVersionCompatibility() { + Version preUnsignedLong = Version.fromId(INTRODUCING_UNSIGNED_LONG.id - SqlVersion.MINOR_MULTIPLIER); + SqlConfiguration sqlConfig = SqlTestUtils.randomConfiguration(SqlVersion.fromId(preUnsignedLong.id)); + analyzer = new Analyzer( + sqlConfig, + functionRegistry, + loadCompatibleIndexResolution("mapping-numeric.json", preUnsignedLong), + new Verifier(new Metrics()) + ); + + String query = "SELECT unsigned_long = 1, unsigned_long::double FROM test"; + String queryWithSubquery = "SELECT l = 1, SQRT(ul) FROM " + + "(SELECT unsigned_long AS ul, long AS l FROM test WHERE ul > 10) WHERE l < 100 "; + + for (String sql : List.of(query, queryWithSubquery)) { + VerificationException ex = expectThrows(VerificationException.class, () -> plan(sql)); + assertThat(ex.getMessage(), containsString("Cannot use field [unsigned_long] with unsupported type [UNSIGNED_LONG]")); + } + } + + public void testNestedUnsignedLongVersionCompatibility() { + String props = """ + { + "properties": { + "container": { + "properties": { + "ul": { + "type": "unsigned_long" + } + } + } + } + } + """; + String sql = "SELECT container.ul as unsigned_long FROM test"; + + Version preUnsignedLong = Version.fromId(INTRODUCING_UNSIGNED_LONG.id - SqlVersion.MINOR_MULTIPLIER); + analyzer = new Analyzer( + SqlTestUtils.randomConfiguration(SqlVersion.fromId(preUnsignedLong.id)), + functionRegistry, + compatibleIndexResolution(props, preUnsignedLong), + new Verifier(new Metrics()) + ); + VerificationException ex = expectThrows(VerificationException.class, () -> plan(sql)); + assertThat(ex.getMessage(), containsString("Cannot use field [container.ul] with unsupported type [UNSIGNED_LONG]")); + + Version postUnsignedLong = Version.fromId(INTRODUCING_UNSIGNED_LONG.id + SqlVersion.MINOR_MULTIPLIER); + for (Version v : List.of(INTRODUCING_UNSIGNED_LONG, postUnsignedLong)) { + analyzer = new Analyzer( + SqlTestUtils.randomConfiguration(SqlVersion.fromId(v.id)), + functionRegistry, + compatibleIndexResolution(props, v), + verifier + ); + LogicalPlan plan = plan(sql); + assertThat(plan, instanceOf(Project.class)); + Project p = (Project) plan; + List projections = p.projections(); + assertThat(projections, hasSize(1)); + Attribute attribute = projections.get(0).toAttribute(); + assertThat(attribute.dataType(), is(UNSIGNED_LONG)); + assertThat(attribute.name(), is("unsigned_long")); + } + } + + public void testUnsignedLongStarExpandedVersionControlled() { + SqlVersion preUnsignedLong = SqlVersion.fromId(INTRODUCING_UNSIGNED_LONG.id - SqlVersion.MINOR_MULTIPLIER); + SqlVersion postUnsignedLong = SqlVersion.fromId(INTRODUCING_UNSIGNED_LONG.id + SqlVersion.MINOR_MULTIPLIER); + String query = "SELECT * FROM test"; + + for (SqlVersion version : List.of(preUnsignedLong, SqlVersion.fromId(INTRODUCING_UNSIGNED_LONG.id), postUnsignedLong)) { + SqlConfiguration config = SqlTestUtils.randomConfiguration(version); + // the mapping is mutated when making it "compatible", so it needs to be reloaded inside the loop. + analyzer = new Analyzer( + config, + functionRegistry, + loadCompatibleIndexResolution("mapping-numeric.json", Version.fromId(version.id)), + new Verifier(new Metrics()) + ); + + LogicalPlan plan = plan(query); + assertThat(plan, instanceOf(Project.class)); + Project p = (Project) plan; + + List projectedDataTypes = p.projections().stream().map(Expression::dataType).toList(); + assertEquals(isTypeSupportedInVersion(UNSIGNED_LONG, Version.fromId(version.id)), projectedDataTypes.contains(UNSIGNED_LONG)); + } + + } + public void testFunctionOverNonExistingFieldAsArgumentAndSameAlias() throws Exception { - Map mapping = TypesTests.loadMapping("mapping-basic.json"); - EsIndex index = new EsIndex("test", mapping); - getIndexResult = IndexResolution.valid(index); - analyzer = new Analyzer(SqlTestUtils.TEST_CFG, functionRegistry, getIndexResult, verifier); + analyzer = new Analyzer(SqlTestUtils.TEST_CFG, functionRegistry, loadIndexResolution("mapping-basic.json"), verifier); VerificationException ex = expectThrows( VerificationException.class, @@ -309,10 +450,7 @@ public void testFunctionOverNonExistingFieldAsArgumentAndSameAlias() throws Exce } public void testFunctionWithExpressionOverNonExistingFieldAsArgumentAndSameAlias() throws Exception { - Map mapping = TypesTests.loadMapping("mapping-basic.json"); - EsIndex index = new EsIndex("test", mapping); - getIndexResult = IndexResolution.valid(index); - analyzer = new Analyzer(SqlTestUtils.TEST_CFG, functionRegistry, getIndexResult, verifier); + analyzer = new Analyzer(SqlTestUtils.TEST_CFG, functionRegistry, loadIndexResolution("mapping-basic.json"), verifier); VerificationException ex = expectThrows( VerificationException.class, @@ -332,4 +470,22 @@ public void testExpandStarOnIndexWithoutColumns() { assertTrue(((Project) plan).projections().isEmpty()); } + private static IndexResolution loadIndexResolution(String mappingName) { + Map mapping = TypesTests.loadMapping(mappingName); + EsIndex index = new EsIndex("test", mapping); + return IndexResolution.valid(index); + } + + private static IndexResolution loadCompatibleIndexResolution(String mappingName, Version version) { + return IndexCompatibility.compatible(loadIndexResolution(mappingName), version); + } + + private static IndexResolution compatibleIndexResolution(String properties, Version version) { + Map mapping = Types.fromEs( + DefaultDataTypeRegistry.INSTANCE, + XContentHelper.convertToMap(JsonXContent.jsonXContent, properties, randomBoolean()) + ); + EsIndex index = new EsIndex("test", mapping); + return IndexCompatibility.compatible(IndexResolution.valid(index), version); + } } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java index cbe3388cd49ff..910de310dbc6d 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java @@ -1457,6 +1457,20 @@ public void testMaxOnKeywordGroupByHavingUnsupported() { ); } + public void testMinOnUnsignedLongGroupByHavingUnsupported() { + assertEquals( + "1:62: HAVING filter is unsupported for function [MIN(unsigned_long)]", + error("SELECT MIN(unsigned_long) min FROM test GROUP BY text HAVING min > 10") + ); + } + + public void testMaxOnUnsignedLongGroupByHavingUnsupported() { + assertEquals( + "1:62: HAVING filter is unsupported for function [MAX(unsigned_long)]", + error("SELECT MAX(unsigned_long) max FROM test GROUP BY text HAVING max > 10") + ); + } + public void testProjectAliasInFilter() { accept("SELECT int AS i FROM test WHERE i > 10"); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractorTests.java index 88773071f465a..ef4b3d66b5fc1 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/CompositeKeyExtractorTests.java @@ -14,24 +14,40 @@ import org.elasticsearch.xpack.sql.AbstractSqlWireSerializingTestCase; import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.querydsl.container.GroupByRef.Property; +import org.elasticsearch.xpack.sql.type.SqlDataTypes; import org.elasticsearch.xpack.sql.util.DateUtils; +import java.math.BigInteger; import java.time.ZoneId; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; +import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; +import static org.elasticsearch.xpack.ql.type.DataTypes.NULL; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; +import static org.elasticsearch.xpack.ql.util.NumericUtils.UNSIGNED_LONG_MAX; import static org.elasticsearch.xpack.sql.util.DateUtils.UTC; public class CompositeKeyExtractorTests extends AbstractSqlWireSerializingTestCase { public static CompositeKeyExtractor randomCompositeKeyExtractor() { - return new CompositeKeyExtractor(randomAlphaOfLength(16), randomFrom(asList(Property.values())), randomZone(), randomBoolean()); + return new CompositeKeyExtractor( + randomAlphaOfLength(16), + randomFrom(asList(Property.values())), + randomZone(), + randomFrom(SqlDataTypes.types()) + ); } public static CompositeKeyExtractor randomCompositeKeyExtractor(ZoneId zoneId) { - return new CompositeKeyExtractor(randomAlphaOfLength(16), randomFrom(asList(Property.values())), zoneId, randomBoolean()); + return new CompositeKeyExtractor( + randomAlphaOfLength(16), + randomFrom(asList(Property.values())), + zoneId, + randomFrom(SqlDataTypes.types()) + ); } @Override @@ -55,18 +71,18 @@ protected CompositeKeyExtractor mutateInstance(CompositeKeyExtractor instance) { instance.key() + "mutated", randomValueOtherThan(instance.property(), () -> randomFrom(Property.values())), randomValueOtherThan(instance.zoneId(), ESTestCase::randomZone), - instance.isDateTimeBased() == false + randomValueOtherThan(instance.dataType(), () -> randomFrom(SqlDataTypes.types())) ); } public void testExtractBucketCount() { Bucket bucket = new TestBucket(emptyMap(), randomLong(), new Aggregations(emptyList())); - CompositeKeyExtractor extractor = new CompositeKeyExtractor(randomAlphaOfLength(16), Property.COUNT, randomZone(), false); + CompositeKeyExtractor extractor = new CompositeKeyExtractor(randomAlphaOfLength(16), Property.COUNT, randomZone(), NULL); assertEquals(bucket.getDocCount(), extractor.extract(bucket)); } public void testExtractKey() { - CompositeKeyExtractor extractor = new CompositeKeyExtractor(randomAlphaOfLength(16), Property.VALUE, UTC, false); + CompositeKeyExtractor extractor = new CompositeKeyExtractor(randomAlphaOfLength(16), Property.VALUE, UTC, NULL); Object value = new Object(); Bucket bucket = new TestBucket(singletonMap(extractor.key(), value), randomLong(), new Aggregations(emptyList())); @@ -74,7 +90,7 @@ public void testExtractKey() { } public void testExtractDate() { - CompositeKeyExtractor extractor = new CompositeKeyExtractor(randomAlphaOfLength(16), Property.VALUE, randomZone(), true); + CompositeKeyExtractor extractor = new CompositeKeyExtractor(randomAlphaOfLength(16), Property.VALUE, randomZone(), DATETIME); long millis = System.currentTimeMillis(); Bucket bucket = new TestBucket(singletonMap(extractor.key(), millis), randomLong(), new Aggregations(emptyList())); @@ -82,7 +98,7 @@ public void testExtractDate() { } public void testExtractIncorrectDateKey() { - CompositeKeyExtractor extractor = new CompositeKeyExtractor(randomAlphaOfLength(16), Property.VALUE, randomZone(), true); + CompositeKeyExtractor extractor = new CompositeKeyExtractor(randomAlphaOfLength(16), Property.VALUE, randomZone(), DATETIME); Object value = new Object(); Bucket bucket = new TestBucket(singletonMap(extractor.key(), value), randomLong(), new Aggregations(emptyList())); @@ -90,6 +106,13 @@ public void testExtractIncorrectDateKey() { assertEquals("Invalid date key returned: " + value, exception.getMessage()); } + public void testExtractUnsignedLong() { + CompositeKeyExtractor extractor = new CompositeKeyExtractor(randomAlphaOfLength(16), Property.VALUE, randomZone(), UNSIGNED_LONG); + Long value = randomLong(); + Bucket bucket = new TestBucket(singletonMap(extractor.key(), value), randomLong(), new Aggregations(emptyList())); + assertEquals(BigInteger.valueOf(value).and(UNSIGNED_LONG_MAX), extractor.extract(bucket)); + } + public static ZoneId extractZoneId(BucketExtractor extractor) { return extractor instanceof CompositeKeyExtractor ? ((CompositeKeyExtractor) extractor).zoneId() : null; } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java index fdb3651745d4c..9edb79211cec9 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/FieldHitExtractorTests.java @@ -34,6 +34,7 @@ import static java.util.Collections.singletonMap; import static org.elasticsearch.common.time.DateUtils.toMilliSeconds; import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.sql.type.SqlDataTypes.GEO_SHAPE; import static org.elasticsearch.xpack.sql.type.SqlDataTypes.SHAPE; import static org.elasticsearch.xpack.sql.util.DateUtils.UTC; @@ -206,6 +207,19 @@ public void testMultipleGeoShapeExtraction() { ); } + public void testUnsignedLongExtraction() { + BigInteger bi = randomBigInteger(); + Number number = bi.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0 ? bi.longValue() : bi; + Object value = randomBoolean() ? number.toString() : number; + + String fieldName = randomAlphaOfLength(10); + DocumentField field = new DocumentField(fieldName, singletonList(value)); + SearchHit hit = new SearchHit(1, null, singletonMap(fieldName, field), null); + FieldHitExtractor fe = new FieldHitExtractor(fieldName, UNSIGNED_LONG, randomZone(), randomBoolean()); + + assertEquals(bi, fe.extract(hit)); + } + private FieldHitExtractor getFieldHitExtractor(String fieldName) { return new FieldHitExtractor(fieldName, null, UTC); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/TopHitsAggExtractorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/TopHitsAggExtractorTests.java index d8d70cf9b4520..c816544a92e2b 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/TopHitsAggExtractorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/execution/search/extractor/TopHitsAggExtractorTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.xpack.sql.type.SqlDataTypes; import org.elasticsearch.xpack.sql.util.DateUtils; +import java.math.BigInteger; import java.time.ZoneId; import java.util.Collections; @@ -103,6 +104,16 @@ public void testExtractDateValue() { assertEquals(DateUtils.asDateTimeWithMillis(value, zoneId), extractor.extract(bucket)); } + public void testExtractUnsignedLong() { + BigInteger bi = randomBigInteger(); + Object value = bi.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0 ? bi.longValue() : bi; + + TopHitsAggExtractor extractor = new TopHitsAggExtractor(randomAlphaOfLength(10), DataTypes.UNSIGNED_LONG, randomZone()); + Aggregation agg = new InternalTopHits(extractor.name(), 0, 1, null, searchHitsOf(value), null); + Bucket bucket = new TestBucket(emptyMap(), 0, new Aggregations(singletonList(agg))); + assertEquals(bi, extractor.extract(bucket)); + } + private SearchHits searchHitsOf(Object value) { TotalHits totalHits = new TotalHits(10, TotalHits.Relation.EQUAL_TO); return new SearchHits( diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathFunctionProcessorTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathFunctionProcessorTests.java index 57bd34d09b61b..56e53bf024504 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathFunctionProcessorTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/expression/function/scalar/math/MathFunctionProcessorTests.java @@ -12,6 +12,8 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.math.MathProcessor.MathOperation; import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; public class MathFunctionProcessorTests extends AbstractWireSerializingTestCase { public static MathProcessor randomMathFunctionProcessor() { @@ -74,4 +76,19 @@ public void testCeil() { assertEquals(4.0, proc.process(3.9)); assertEquals(-12.0, proc.process(-12.1)); } + + public void testUnsignedLongAbs() { + MathProcessor proc = new MathProcessor(MathOperation.ABS); + BigInteger bi = randomBigInteger(); + assertEquals(bi, proc.process(bi)); + } + + public void testUnsignedLongSign() { + MathProcessor proc = new MathProcessor(MathOperation.SIGN); + for (BigInteger bi : Arrays.asList(BigInteger.valueOf(randomNonNegativeLong()), BigInteger.ZERO)) { + Object val = proc.process(bi); + assertEquals(bi.intValue() == 0 ? 0 : 1, val); + assertTrue(val instanceof Integer); + } + } } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java index 74759812bbd7c..fe307baa24f0e 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/optimizer/OptimizerTests.java @@ -51,6 +51,7 @@ import org.elasticsearch.xpack.ql.plan.logical.OrderBy; import org.elasticsearch.xpack.ql.plan.logical.Project; import org.elasticsearch.xpack.ql.tree.Source; +import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.ql.type.DataTypes; import org.elasticsearch.xpack.ql.type.EsField; import org.elasticsearch.xpack.ql.util.CollectionUtils; @@ -143,6 +144,7 @@ import static org.elasticsearch.xpack.ql.type.DataTypes.INTEGER; import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD; import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.sql.SqlTestUtils.literal; import static org.elasticsearch.xpack.sql.type.SqlDataTypes.DATE; import static org.elasticsearch.xpack.sql.util.DateUtils.UTC; @@ -873,60 +875,64 @@ public void testCombineUnbalancedComparisonsMixedWithEqualsIntoRange() { } public void testTranslateMinToFirst() { - Min min1 = new Min(EMPTY, new FieldAttribute(EMPTY, "str", new EsField("str", KEYWORD, emptyMap(), true))); - Min min2 = new Min(EMPTY, getFieldAttribute()); + for (DataType dataType : List.of(KEYWORD, UNSIGNED_LONG)) { + Min min1 = new Min(EMPTY, new FieldAttribute(EMPTY, "field", new EsField("field", dataType, emptyMap(), true))); + Min min2 = new Min(EMPTY, getFieldAttribute()); - OrderBy plan = new OrderBy( - EMPTY, - new Aggregate(EMPTY, FROM(), emptyList(), asList(a("min1", min1), a("min2", min2))), - asList( - new Order(EMPTY, min1, OrderDirection.ASC, Order.NullsPosition.LAST), - new Order(EMPTY, min2, OrderDirection.ASC, Order.NullsPosition.LAST) - ) - ); - LogicalPlan result = new ReplaceMinMaxWithTopHits().apply(plan); - assertTrue(result instanceof OrderBy); - List order = ((OrderBy) result).order(); - assertEquals(2, order.size()); - assertEquals(First.class, order.get(0).child().getClass()); - assertEquals(min2, order.get(1).child()); - First first = (First) order.get(0).child(); - - assertTrue(((OrderBy) result).child() instanceof Aggregate); - List aggregates = ((Aggregate) ((OrderBy) result).child()).aggregates(); - assertEquals(2, aggregates.size()); - assertEquals(Alias.class, aggregates.get(0).getClass()); - assertEquals(Alias.class, aggregates.get(1).getClass()); - assertSame(first, ((Alias) aggregates.get(0)).child()); - assertEquals(min2, ((Alias) aggregates.get(1)).child()); + OrderBy plan = new OrderBy( + EMPTY, + new Aggregate(EMPTY, FROM(), emptyList(), asList(a("min1", min1), a("min2", min2))), + asList( + new Order(EMPTY, min1, OrderDirection.ASC, Order.NullsPosition.LAST), + new Order(EMPTY, min2, OrderDirection.ASC, Order.NullsPosition.LAST) + ) + ); + LogicalPlan result = new ReplaceMinMaxWithTopHits().apply(plan); + assertTrue(result instanceof OrderBy); + List order = ((OrderBy) result).order(); + assertEquals(2, order.size()); + assertEquals(First.class, order.get(0).child().getClass()); + assertEquals(min2, order.get(1).child()); + First first = (First) order.get(0).child(); + + assertTrue(((OrderBy) result).child() instanceof Aggregate); + List aggregates = ((Aggregate) ((OrderBy) result).child()).aggregates(); + assertEquals(2, aggregates.size()); + assertEquals(Alias.class, aggregates.get(0).getClass()); + assertEquals(Alias.class, aggregates.get(1).getClass()); + assertSame(first, ((Alias) aggregates.get(0)).child()); + assertEquals(min2, ((Alias) aggregates.get(1)).child()); + } } public void testTranslateMaxToLast() { - Max max1 = new Max(EMPTY, new FieldAttribute(EMPTY, "str", new EsField("str", KEYWORD, emptyMap(), true))); - Max max2 = new Max(EMPTY, getFieldAttribute()); + for (DataType dataType : List.of(KEYWORD, UNSIGNED_LONG)) { + Max max1 = new Max(EMPTY, new FieldAttribute(EMPTY, "field", new EsField("field", dataType, emptyMap(), true))); + Max max2 = new Max(EMPTY, getFieldAttribute()); - OrderBy plan = new OrderBy( - EMPTY, - new Aggregate(EMPTY, FROM(), emptyList(), asList(a("max1", max1), a("max2", max2))), - asList( - new Order(EMPTY, max1, OrderDirection.ASC, Order.NullsPosition.LAST), - new Order(EMPTY, max2, OrderDirection.ASC, Order.NullsPosition.LAST) - ) - ); - LogicalPlan result = new ReplaceMinMaxWithTopHits().apply(plan); - assertTrue(result instanceof OrderBy); - List order = ((OrderBy) result).order(); - assertEquals(Last.class, order.get(0).child().getClass()); - assertEquals(max2, order.get(1).child()); - Last last = (Last) order.get(0).child(); - - assertTrue(((OrderBy) result).child() instanceof Aggregate); - List aggregates = ((Aggregate) ((OrderBy) result).child()).aggregates(); - assertEquals(2, aggregates.size()); - assertEquals(Alias.class, aggregates.get(0).getClass()); - assertEquals(Alias.class, aggregates.get(1).getClass()); - assertSame(last, ((Alias) aggregates.get(0)).child()); - assertEquals(max2, ((Alias) aggregates.get(1)).child()); + OrderBy plan = new OrderBy( + EMPTY, + new Aggregate(EMPTY, FROM(), emptyList(), asList(a("max1", max1), a("max2", max2))), + asList( + new Order(EMPTY, max1, OrderDirection.ASC, Order.NullsPosition.LAST), + new Order(EMPTY, max2, OrderDirection.ASC, Order.NullsPosition.LAST) + ) + ); + LogicalPlan result = new ReplaceMinMaxWithTopHits().apply(plan); + assertTrue(result instanceof OrderBy); + List order = ((OrderBy) result).order(); + assertEquals(Last.class, order.get(0).child().getClass()); + assertEquals(max2, order.get(1).child()); + Last last = (Last) order.get(0).child(); + + assertTrue(((OrderBy) result).child() instanceof Aggregate); + List aggregates = ((Aggregate) ((OrderBy) result).child()).aggregates(); + assertEquals(2, aggregates.size()); + assertEquals(Alias.class, aggregates.get(0).getClass()); + assertEquals(Alias.class, aggregates.get(1).getClass()); + assertSame(last, ((Alias) aggregates.get(0)).child()); + assertEquals(max2, ((Alias) aggregates.get(1)).child()); + } } public void testSortAggregateOnOrderByWithTwoFields() { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumnsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumnsTests.java new file mode 100644 index 0000000000000..2efd00f0e111d --- /dev/null +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumnsTests.java @@ -0,0 +1,97 @@ +/* + * 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.sql.plan.logical.command; + +import org.elasticsearch.Version; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.ql.index.IndexCompatibility; +import org.elasticsearch.xpack.ql.type.EsField; +import org.elasticsearch.xpack.sql.proto.SqlVersion; + +import java.sql.JDBCType; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static java.util.Arrays.asList; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.supportsUnsignedLong; +import static org.elasticsearch.xpack.ql.type.DataTypes.BOOLEAN; +import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; +import static org.elasticsearch.xpack.ql.type.DataTypes.FLOAT; +import static org.elasticsearch.xpack.ql.type.DataTypes.INTEGER; +import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD; +import static org.elasticsearch.xpack.ql.type.DataTypes.NESTED; +import static org.elasticsearch.xpack.ql.type.DataTypes.OBJECT; +import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSUPPORTED; +import static org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumnsTests.UNSIGNED_LONG_TEST_VERSIONS; +import static org.elasticsearch.xpack.sql.type.SqlDataTypes.GEO_POINT; +import static org.elasticsearch.xpack.sql.type.SqlDataTypes.GEO_SHAPE; +import static org.elasticsearch.xpack.sql.types.SqlTypesTests.loadMapping; + +public class ShowColumnsTests extends ESTestCase { + + public static final String JDBC_TYPE_GEOMETRY = "GEOMETRY"; + + public void testShowColumns() { + String prefix = "myIndex"; + List> rows = new ArrayList<>(); + ShowColumns.fillInRows(loadMapping("mapping-multi-field-variation.json", true), prefix, rows); + + List> expect = asList( + asList("bool", JDBCType.BOOLEAN.getName(), BOOLEAN.typeName()), + asList("int", JDBCType.INTEGER.getName(), INTEGER.typeName()), + asList("unsigned_long", JDBCType.NUMERIC.getName(), UNSIGNED_LONG.typeName()), + asList("float", JDBCType.REAL.getName(), FLOAT.typeName()), + asList("text", JDBCType.VARCHAR.getName(), TEXT.typeName()), + asList("keyword", JDBCType.VARCHAR.getName(), KEYWORD.typeName()), + asList("date", JDBCType.TIMESTAMP.getName(), DATETIME.typeName()), + asList("date_nanos", JDBCType.TIMESTAMP.getName(), DATETIME.typeName()), + asList("unsupported", JDBCType.OTHER.getName(), UNSUPPORTED.typeName()), + asList("some", JDBCType.STRUCT.getName(), OBJECT.typeName()), + asList("some.dotted", JDBCType.STRUCT.getName(), OBJECT.typeName()), + asList("some.dotted.field", JDBCType.VARCHAR.getName(), KEYWORD.typeName()), + asList("some.string", JDBCType.VARCHAR.getName(), TEXT.typeName()), + asList("some.string.normalized", JDBCType.VARCHAR.getName(), KEYWORD.typeName()), + asList("some.string.typical", JDBCType.VARCHAR.getName(), KEYWORD.typeName()), + asList("some.ambiguous", JDBCType.VARCHAR.getName(), TEXT.typeName()), + asList("some.ambiguous.one", JDBCType.VARCHAR.getName(), KEYWORD.typeName()), + asList("some.ambiguous.two", JDBCType.VARCHAR.getName(), KEYWORD.typeName()), + asList("some.ambiguous.normalized", JDBCType.VARCHAR.getName(), KEYWORD.typeName()), + asList("foo_type", JDBCType.OTHER.getName(), UNSUPPORTED.typeName()), + asList("point", JDBC_TYPE_GEOMETRY, GEO_POINT.typeName()), + asList("shape", JDBC_TYPE_GEOMETRY, GEO_SHAPE.typeName()), + asList("nested", JDBCType.STRUCT.getName(), NESTED.typeName()), + asList("nested.point", JDBC_TYPE_GEOMETRY, GEO_POINT.typeName()) + ); + + assertEquals(expect.size(), rows.size()); + assertEquals(expect.get(0).size(), rows.get(0).size()); + + for (int i = 0; i < expect.size(); i++) { + List expectedRow = expect.get(i); + List receivedRow = rows.get(i); + assertEquals("Name mismatch in row " + i, prefix + "." + expectedRow.get(0), receivedRow.get(0)); + assertEquals("Type mismatch in row " + i, expectedRow.get(1), receivedRow.get(1)); + assertEquals("Mapping mismatch in row " + i, expectedRow.get(2), receivedRow.get(2)); + } + } + + public void testUnsignedLongFiltering() { + List rowSupported = List.of("unsigned_long", "NUMERIC", "unsigned_long"); + List rowUnsupported = List.of("unsigned_long", "OTHER", "unsupported"); + for (SqlVersion version : UNSIGNED_LONG_TEST_VERSIONS) { + List> rows = new ArrayList<>(); + // mapping's mutated by IndexCompatibility.compatible, needs to stay in the loop + Map mapping = loadMapping("mapping-multi-field-variation.json", true); + ShowColumns.fillInRows(IndexCompatibility.compatible(mapping, Version.fromId(version.id)), null, rows); + assertTrue((supportsUnsignedLong(Version.fromId(version.id)) && rows.contains(rowSupported)) || rows.contains(rowUnsupported)); + } + } +} diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumnsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumnsTests.java index 48b851a322ccc..e96263af9f168 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumnsTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysColumnsTests.java @@ -12,6 +12,7 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.ql.expression.function.FunctionRegistry; import org.elasticsearch.xpack.ql.index.EsIndex; +import org.elasticsearch.xpack.ql.index.IndexCompatibility; import org.elasticsearch.xpack.ql.index.IndexResolution; import org.elasticsearch.xpack.ql.index.IndexResolver; import org.elasticsearch.xpack.ql.type.EsField; @@ -38,11 +39,15 @@ import java.util.Map; import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.elasticsearch.action.ActionListener.wrap; import static org.elasticsearch.xpack.ql.TestUtils.UTC; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.INTRODUCING_UNSIGNED_LONG; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.isTypeSupportedInVersion; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.sql.proto.Mode.isDriver; import static org.elasticsearch.xpack.sql.types.SqlTypesTests.loadMapping; import static org.mockito.ArgumentMatchers.any; @@ -53,11 +58,18 @@ public class SysColumnsTests extends ESTestCase { + public static List UNSIGNED_LONG_TEST_VERSIONS = List.of( + SqlVersion.fromId(INTRODUCING_UNSIGNED_LONG.id - SqlVersion.MINOR_MULTIPLIER), + SqlVersion.fromId(INTRODUCING_UNSIGNED_LONG.id), + SqlVersion.fromId(INTRODUCING_UNSIGNED_LONG.id + SqlVersion.MINOR_MULTIPLIER), + SqlVersion.fromId(Version.CURRENT.id) + ); + private static final String CLUSTER_NAME = "cluster"; private static final Map MAPPING1 = loadMapping("mapping-multi-field-with-nested.json", true); private static final Map MAPPING2 = loadMapping("mapping-multi-field-variation.json", true); - private static final int FIELD_COUNT1 = 19; - private static final int FIELD_COUNT2 = 17; + private static final int FIELD_COUNT1 = 20; + private static final int FIELD_COUNT2 = 18; private final SqlParser parser = new SqlParser(); @@ -68,49 +80,53 @@ private void sysColumnsInMode(Mode mode) { assertEquals(FIELD_COUNT2, rows.size()); assertEquals(24, rows.get(0).size()); - List row = rows.get(0); + int index = 0; + List row = rows.get(index++); assertDriverType("bool", Types.BOOLEAN, false, 1, 1, typeClass, row); - row = rows.get(1); + row = rows.get(index++); assertDriverType("int", Types.INTEGER, true, 11, 4, typeClass, row); - row = rows.get(2); + row = rows.get(index++); + assertDriverType("unsigned_long", Types.NUMERIC, true, 20, Long.BYTES, typeClass, row); + + row = rows.get(index++); assertDriverType("float", Types.REAL, true, 15, 4, typeClass, row); - row = rows.get(3); + row = rows.get(index++); assertDriverType("text", Types.VARCHAR, false, Integer.MAX_VALUE, Integer.MAX_VALUE, typeClass, row); - row = rows.get(4); + row = rows.get(index++); assertDriverType("keyword", Types.VARCHAR, false, Short.MAX_VALUE - 1, Integer.MAX_VALUE, typeClass, row); - row = rows.get(5); + row = rows.get(index++); assertDriverType("date", Types.TIMESTAMP, false, 34, 8, typeClass, row); - row = rows.get(6); + row = rows.get(index++); assertDriverType("date_nanos", Types.TIMESTAMP, false, 34, 8, typeClass, row); - row = rows.get(7); + row = rows.get(index++); assertDriverType("some.dotted.field", Types.VARCHAR, false, Short.MAX_VALUE - 1, Integer.MAX_VALUE, typeClass, row); - row = rows.get(8); + row = rows.get(index++); assertDriverType("some.string", Types.VARCHAR, false, Integer.MAX_VALUE, Integer.MAX_VALUE, typeClass, row); - row = rows.get(9); + row = rows.get(index++); assertDriverType("some.string.normalized", Types.VARCHAR, false, Short.MAX_VALUE - 1, Integer.MAX_VALUE, typeClass, row); - row = rows.get(10); + row = rows.get(index++); assertDriverType("some.string.typical", Types.VARCHAR, false, Short.MAX_VALUE - 1, Integer.MAX_VALUE, typeClass, row); - row = rows.get(11); + row = rows.get(index++); assertDriverType("some.ambiguous", Types.VARCHAR, false, Integer.MAX_VALUE, Integer.MAX_VALUE, typeClass, row); - row = rows.get(12); + row = rows.get(index++); assertDriverType("some.ambiguous.one", Types.VARCHAR, false, Short.MAX_VALUE - 1, Integer.MAX_VALUE, typeClass, row); - row = rows.get(13); + row = rows.get(index++); assertDriverType("some.ambiguous.two", Types.VARCHAR, false, Short.MAX_VALUE - 1, Integer.MAX_VALUE, typeClass, row); - row = rows.get(14); + row = rows.get(index++); assertDriverType("some.ambiguous.normalized", Types.VARCHAR, false, Short.MAX_VALUE - 1, Integer.MAX_VALUE, typeClass, row); } @@ -130,6 +146,30 @@ public void testInNonDriverMode() { } } + public void testUnsignedLongFiltering() { + for (Mode mode : List.of(Mode.JDBC, Mode.ODBC)) { + for (SqlVersion version : UNSIGNED_LONG_TEST_VERSIONS) { + List> rows = new ArrayList<>(); + // mapping's mutated by IndexCompatibility.compatible, needs to stay in the loop + Map mapping = loadMapping("mapping-multi-field-variation.json", true); + SysColumns.fillInRows( + "test", + "index", + IndexCompatibility.compatible(mapping, Version.fromId(version.id)), + null, + rows, + null, + mode + ); + List types = rows.stream().map(row -> name(row).toString()).collect(Collectors.toList()); + assertEquals( + isTypeSupportedInVersion(UNSIGNED_LONG, Version.fromId(version.id)), + types.contains(UNSIGNED_LONG.toString().toLowerCase(Locale.ROOT)) + ); + } + } + } + private static Object name(List list) { return list.get(3); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypesTests.java index 2b71a5b04a103..44ee3741a2d1d 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypesTests.java @@ -28,10 +28,16 @@ import org.elasticsearch.xpack.sql.util.DateUtils; import java.sql.JDBCType; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static java.util.Arrays.asList; import static org.elasticsearch.action.ActionListener.wrap; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.isTypeSupportedInVersion; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; +import static org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumnsTests.UNSIGNED_LONG_TEST_VERSIONS; import static org.mockito.Mockito.mock; public class SysTypesTests extends ESTestCase { @@ -78,6 +84,7 @@ public void testSysTypes() { "LONG", "BINARY", "NULL", + "UNSIGNED_LONG", "INTEGER", "SHORT", "HALF_FLOAT", @@ -132,6 +139,26 @@ public void testSysTypes() { }, ex -> fail(ex.getMessage()))); } + public void testUnsignedLongFiltering() { + Set versions = new HashSet<>(UNSIGNED_LONG_TEST_VERSIONS); + versions.add(null); + for (SqlVersion version : versions) { + for (Mode mode : Mode.values()) { + Tuple cmd = sql("SYS TYPES", mode, version); + + cmd.v1().execute(cmd.v2(), wrap(p -> { + SchemaRowSet r = (SchemaRowSet) p.rowSet(); + List types = new ArrayList<>(); + r.forEachRow(rv -> types.add((String) rv.column(0))); + assertEquals( + isTypeSupportedInVersion(UNSIGNED_LONG, Version.fromId(cmd.v2().configuration().version().id)), + types.contains(UNSIGNED_LONG.toString()) + ); + }, ex -> fail(ex.getMessage()))); + } + } + } + public void testSysTypesDefaultFiltering() { Tuple cmd = sql("SYS TYPES 0"); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorSpecTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorSpecTests.java index a528280414c09..3cc1023e37d1f 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorSpecTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/QueryTranslatorSpecTests.java @@ -15,7 +15,6 @@ import org.elasticsearch.xpack.ql.index.IndexResolution; import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan; import org.elasticsearch.xpack.ql.type.EsField; -import org.elasticsearch.xpack.sql.SqlTestUtils; import org.elasticsearch.xpack.sql.analysis.analyzer.Analyzer; import org.elasticsearch.xpack.sql.analysis.analyzer.Verifier; import org.elasticsearch.xpack.sql.expression.function.SqlFunctionRegistry; @@ -33,6 +32,8 @@ import java.util.List; import java.util.Map; +import static org.elasticsearch.xpack.sql.SqlTestUtils.TEST_CFG; + public class QueryTranslatorSpecTests extends ESTestCase { private static final List TEST_FILENAMES = List.of("querytranslator_tests.txt", "querytranslator_subqueries_tests.txt"); @@ -48,7 +49,7 @@ private static class TestContext { Map mapping = SqlTypesTests.loadMapping(mappingFile); EsIndex test = new EsIndex("test", mapping); IndexResolution getIndexResult = IndexResolution.valid(test); - analyzer = new Analyzer(SqlTestUtils.TEST_CFG, new SqlFunctionRegistry(), getIndexResult, new Verifier(new Metrics())); + analyzer = new Analyzer(TEST_CFG, new SqlFunctionRegistry(), getIndexResult, new Verifier(new Metrics())); optimizer = new Optimizer(); planner = new Planner(); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/SqlDataTypeConverterTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/SqlDataTypeConverterTests.java index ae4acf2ffeb6e..490fb935308d2 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/SqlDataTypeConverterTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/SqlDataTypeConverterTests.java @@ -16,6 +16,7 @@ import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.sql.util.DateUtils; +import java.math.BigInteger; import java.time.OffsetTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -33,6 +34,7 @@ import static org.elasticsearch.xpack.ql.type.DataTypes.NULL; import static org.elasticsearch.xpack.ql.type.DataTypes.SHORT; import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT; +import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; import static org.elasticsearch.xpack.ql.type.DataTypes.UNSUPPORTED; import static org.elasticsearch.xpack.sql.type.SqlDataTypeConverter.commonType; import static org.elasticsearch.xpack.sql.type.SqlDataTypeConverter.converterFor; @@ -151,6 +153,16 @@ public void testConversionToDate() { Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(Double.MAX_VALUE)); assertEquals("[" + Double.MAX_VALUE + "] out of [long] range", e.getMessage()); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + BigInteger bi = BigInteger.valueOf(randomNonNegativeLong()); + assertEquals(date(bi.longValue()), conversion.convert(bi)); + + BigInteger tooLarge = bi.add(BigInteger.valueOf(Long.MAX_VALUE)); + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(tooLarge)); + assertEquals("[" + tooLarge + "] out of [long] range", e.getMessage()); + } { Converter conversion = converterFor(INTEGER, to); assertNull(conversion.convert(null)); @@ -223,6 +235,16 @@ public void testConversionToTime() { Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(Double.MAX_VALUE)); assertEquals("[" + Double.MAX_VALUE + "] out of [long] range", e.getMessage()); } + { + Converter conversion = converterFor(UNSIGNED_LONG, to); + assertNull(conversion.convert(null)); + BigInteger bi = BigInteger.valueOf(randomNonNegativeLong()); + assertEquals(time(bi.longValue()), conversion.convert(bi)); + + BigInteger tooLarge = bi.add(BigInteger.valueOf(Long.MAX_VALUE)); + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(tooLarge)); + assertEquals("[" + tooLarge + "] out of [long] range", e.getMessage()); + } { Converter conversion = converterFor(INTEGER, to); assertNull(conversion.convert(null)); @@ -513,6 +535,30 @@ public void testConversionToBoolean() { } } + public void testConversionToUnsignedLong() { + DataType to = UNSIGNED_LONG; + { + Converter conversion = converterFor(DATE, to); + assertNull(conversion.convert(null)); + + long l = randomNonNegativeLong(); + ZonedDateTime zdt = asDateOnly(l); + assertEquals(BigInteger.valueOf(zdt.toEpochSecond() * 1000), conversion.convert(zdt)); + + ZonedDateTime zdtn = asDateOnly(-l); + Exception e = expectThrows(QlIllegalArgumentException.class, () -> conversion.convert(zdtn)); + assertEquals("[" + zdtn.toEpochSecond() * 1000 + "] out of [unsigned_long] range", e.getMessage()); + } + { + Converter conversion = converterFor(TIME, to); + assertNull(conversion.convert(null)); + + long l = randomLong(); + OffsetTime ot = asTimeOnly(l); + assertEquals(BigInteger.valueOf(ot.atDate(DateUtils.EPOCH).toInstant().toEpochMilli()), conversion.convert(ot)); + } + } + public void testConversionToInt() { DataType to = INTEGER; {