diff --git a/docs/changelog/85502.yaml b/docs/changelog/85502.yaml new file mode 100644 index 0000000000000..b812505c0a9bc --- /dev/null +++ b/docs/changelog/85502.yaml @@ -0,0 +1,6 @@ +pr: 85502 +summary: Add support for VERSION field type in SQL and EQL +area: Query Languages +type: enhancement +issues: + - 83375 diff --git a/docs/reference/sql/language/data-types.asciidoc b/docs/reference/sql/language/data-types.asciidoc index 948f7cb8c60ba..a2a89deb745e6 100644 --- a/docs/reference/sql/language/data-types.asciidoc +++ b/docs/reference/sql/language/data-types.asciidoc @@ -28,6 +28,7 @@ s|SQL precision | <> | binary | VARBINARY | 2,147,483,647 | <> | datetime | TIMESTAMP | 29 | <> | ip | VARCHAR | 39 +| <> | version | VARCHAR | 32,766 4+h| Complex types diff --git a/x-pack/plugin/eql/qa/common/src/main/resources/data/extra.data b/x-pack/plugin/eql/qa/common/src/main/resources/data/extra.data index 8f9bcd08993cd..d87ed34992cd8 100644 --- a/x-pack/plugin/eql/qa/common/src/main/resources/data/extra.data +++ b/x-pack/plugin/eql/qa/common/src/main/resources/data/extra.data @@ -112,6 +112,7 @@ "transID": 2, "process.entity_id": 512, "process.pid": 123, + "version": "1.5.0", "sequence": 18 }, { @@ -120,6 +121,7 @@ "transID": 0, "process.entity_id": 512, "process.pid": 123, + "version": "1.5.0", "sequence": 19 }, { @@ -128,6 +130,7 @@ "transID": 0, "process.entity_id": 512, "process.pid": 123, + "version": "1.2.4", "sequence": 20 }, { @@ -136,6 +139,7 @@ "transID": 0, "process.entity_id": 512, "process.pid": 123, + "version": "1.11.3", "sequence": 21 }, { @@ -144,6 +148,7 @@ "transID": 0, "process.entity_id": 512, "process.pid": 123, + "version": "bad", "sequence": 22 }, { @@ -152,6 +157,7 @@ "transID": 0, "process.entity_id": 512, "process.pid": 123, + "version": "1.2.4-SNAPSHOT", "sequence": 23 } ] diff --git a/x-pack/plugin/eql/qa/common/src/main/resources/data/extra.mapping b/x-pack/plugin/eql/qa/common/src/main/resources/data/extra.mapping index 44fa94b97ce07..2fde8cf52fdd8 100644 --- a/x-pack/plugin/eql/qa/common/src/main/resources/data/extra.mapping +++ b/x-pack/plugin/eql/qa/common/src/main/resources/data/extra.mapping @@ -27,6 +27,9 @@ "optional_field_default_null": { "type": "keyword", "null_value": "NULL" + }, + "version": { + "type": "version" } } } diff --git a/x-pack/plugin/eql/qa/common/src/main/resources/test_extra.toml b/x-pack/plugin/eql/qa/common/src/main/resources/test_extra.toml index 3f8fdb6a1ae50..14352f067b43d 100644 --- a/x-pack/plugin/eql/qa/common/src/main/resources/test_extra.toml +++ b/x-pack/plugin/eql/qa/common/src/main/resources/test_extra.toml @@ -76,7 +76,7 @@ expected_event_ids = [] [[queries]] name = "sequenceOptionalFieldAsKeyAndFirstFilter" query = ''' -sequence by ?x +sequence by ?x [OPTIONAL where ?x == null] [OPTIONAL where true] ''' @@ -137,7 +137,7 @@ expected_event_ids = [12,13,14, join_keys = ["null","123", "null","null", "512","123"] - + [[queries]] name = "sequenceOneKeyOptional_1" query = ''' @@ -159,7 +159,7 @@ expected_event_ids = [12,13,14, 18,19,20] join_keys = ["null","123", "512","123"] - + [[queries]] name = "sequenceNoOptionalKeys" query = ''' @@ -195,7 +195,7 @@ expected_event_ids = [12,13,14, join_keys = ["null","123", "null","null", "512","123"] - + [[queries]] name = "sequenceWithOptionalFieldsAndUntil_2" query = ''' @@ -209,3 +209,65 @@ expected_event_ids = [12,13,14, join_keys = ["null","123", "null","null"] +[[queries]] +name = "sequenceWithVersionCheck" +query = ''' + sequence by transID + [ file where version == "1.2.4" ] + [ file where version == "1.11.3" ] +''' +expected_event_ids = [20, 21] +join_keys = ["0"] + +[[queries]] +name = "sequenceWithVersionRangeCheck" +query = ''' + sequence by transID + [ file where version == "1.2.4" ] + [ file where version > "1.5.0" ] +''' +expected_event_ids = [20, 21] +join_keys = ["0"] + +[[queries]] +name = "sequenceWithVersionRangeCheck2" +query = ''' + sequence by transID + [ file where version == "1.2.4" ] + [ file where version < "1.5.0" ] +''' +expected_event_ids = [20, 23] +join_keys = ["0"] + + +[[queries]] +name = "sequenceWithInvalidVersion" +query = ''' + sequence by transID + [ file where version == "1.2.4" ] + [ file where version == "bad" ] +''' +expected_event_ids = [20, 22] +join_keys = ["0"] + + +[[queries]] +name = "sequenceWithVersionConcat" +query = ''' + sequence by transID + [ file where CONCAT(version, "") == "1.2.4" ] + [ file where version == "bad" ] +''' +expected_event_ids = [20, 22] +join_keys = ["0"] + + +[[queries]] +name = "sequenceWithVersionJoinKey" +query = ''' + sequence by version + [ process where true ] + [ file where true ] +''' +expected_event_ids = [18, 19] +join_keys = ["1.5.0"] diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/execution/search/SourceGenerator.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/execution/search/SourceGenerator.java index 539a49a8df580..ab1fda7bcdfd8 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/execution/search/SourceGenerator.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/execution/search/SourceGenerator.java @@ -11,7 +11,6 @@ import org.elasticsearch.search.fetch.subphase.FieldAndFormat; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.NestedSortBuilder; -import org.elasticsearch.search.sort.ScriptSortBuilder.ScriptSortType; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.xpack.eql.querydsl.container.QueryContainer; import org.elasticsearch.xpack.ql.execution.search.QlSourceBuilder; @@ -133,10 +132,7 @@ private static void sorting(QueryContainer container, SearchSourceBuilder source } } } else if (sortable instanceof ScriptSort ss) { - sortBuilder = scriptSort( - ss.script().toPainless(), - ss.script().outputType().isNumeric() ? ScriptSortType.NUMBER : ScriptSortType.STRING - ); + sortBuilder = scriptSort(ss.script().toPainless(), ss.script().outputType().scriptSortType()); } if (sortBuilder != null) { diff --git a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPainlessExtension.java b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPainlessExtension.java index 580e7ff4c07dc..77e3c0aa9ad64 100644 --- a/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPainlessExtension.java +++ b/x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/plugin/EqlPainlessExtension.java @@ -11,6 +11,7 @@ import org.elasticsearch.painless.spi.WhitelistLoader; import org.elasticsearch.script.AggregationScript; import org.elasticsearch.script.BucketAggregationSelectorScript; +import org.elasticsearch.script.BytesRefSortScript; import org.elasticsearch.script.FieldScript; import org.elasticsearch.script.FilterScript; import org.elasticsearch.script.NumberSortScript; @@ -36,6 +37,7 @@ public Map, List> getContextWhitelists() { whitelist.put(FieldScript.CONTEXT, list); whitelist.put(NumberSortScript.CONTEXT, list); whitelist.put(StringSortScript.CONTEXT, list); + whitelist.put(BytesRefSortScript.CONTEXT, list); whitelist.put(BucketAggregationSelectorScript.CONTEXT, list); return whitelist; } 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 5abd4dfee9f82..48a2d56a3e25f 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 @@ -93,6 +93,7 @@ public void testQueryCondition() { assertEquals("1:11: Condition expression needs to be boolean, found [TEXT]", error("any where hostname")); assertEquals("1:11: Condition expression needs to be boolean, found [KEYWORD]", error("any where constant_keyword")); assertEquals("1:11: Condition expression needs to be boolean, found [IP]", error("any where source_address")); + assertEquals("1:11: Condition expression needs to be boolean, found [VERSION]", error("any where version")); } public void testQueryStartsWithNumber() { @@ -363,6 +364,11 @@ public void testIP() { accept(idxr, "foo where ip_addr == 0"); } + public void testVersion() { + final IndexResolution idxr = loadIndexResolution("mapping-version.json"); + accept(idxr, "foo where version_number == \"2.1.4\""); + } + public void testJoin() { final IndexResolution idxr = loadIndexResolution("mapping-join.json"); accept(idxr, "foo where serial_event_id == 0"); diff --git a/x-pack/plugin/eql/src/test/resources/mapping-default.json b/x-pack/plugin/eql/src/test/resources/mapping-default.json index 29a5235a03add..b7f44cbddb8ed 100644 --- a/x-pack/plugin/eql/src/test/resources/mapping-default.json +++ b/x-pack/plugin/eql/src/test/resources/mapping-default.json @@ -90,6 +90,9 @@ }, "bool" : { "type" : "boolean" + }, + "version" : { + "type" : "version" } } } diff --git a/x-pack/plugin/eql/src/test/resources/mapping-version.json b/x-pack/plugin/eql/src/test/resources/mapping-version.json new file mode 100644 index 0000000000000..3f0d1cf708c03 --- /dev/null +++ b/x-pack/plugin/eql/src/test/resources/mapping-version.json @@ -0,0 +1,17 @@ +{ + "properties" : { + "event" : { + "properties" : { + "category" : { + "type" : "keyword" + } + } + }, + "@timestamp" : { + "type" : "date" + }, + "version_number" : { + "type" : "version" + } + } +} diff --git a/x-pack/plugin/eql/src/test/resources/querytranslator_tests.txt b/x-pack/plugin/eql/src/test/resources/querytranslator_tests.txt index 676266262be53..e72260fbffc52 100644 --- a/x-pack/plugin/eql/src/test/resources/querytranslator_tests.txt +++ b/x-pack/plugin/eql/src/test/resources/querytranslator_tests.txt @@ -901,3 +901,17 @@ process where pid > 100 and pid < 200 // the "not" has an undefined location of where it should be used in the Painless script // disjunctionInsideFunctionWithNot // process where string(pid > 5 and pid != 10) == \"true\" + + +versionFieldAutomaticConversion +process where version > "2" +; +{"range":{"version":{"gt":"2" +; + +versionFieldCast +process where CONCAT(version, constant_keyword) > "2" +; +{"script":{"source":"InternalEqlScriptUtils.multiValueDocValues(doc,params.v0,X0->InternalEqlScriptUtils.multiValueDocValues(doc,params.v1,X1->InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.gt(InternalEqlScriptUtils.concat([X0,X1]),params.v2))))" +"params":{"v0":"version","v1":"constant_keyword","v2":"2"}} +; diff --git a/x-pack/plugin/ql/build.gradle b/x-pack/plugin/ql/build.gradle index 81ef6a52be28e..153a8bbafe4bd 100644 --- a/x-pack/plugin/ql/build.gradle +++ b/x-pack/plugin/ql/build.gradle @@ -16,6 +16,7 @@ archivesBaseName = 'x-pack-ql' dependencies { api "org.antlr:antlr4-runtime:${antlrVersion}" + api project(path: xpackModule('mapper-version')) compileOnly project(path: xpackModule('core')) testApi(project(xpackModule('ql:test-fixtures'))) { exclude group: 'org.elasticsearch.plugin', module: 'ql' 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 cbe032c253ad8..889f073fc6413 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,6 +6,8 @@ */ package org.elasticsearch.xpack.ql.expression.predicate.operator.comparison; +import org.elasticsearch.xpack.versionfield.Version; + import java.math.BigInteger; import java.util.Set; @@ -71,13 +73,21 @@ static Integer compare(Object l, Object r) { return null; } // typical number comparison - if (l instanceof Number && r instanceof Number) { - return compare((Number) l, (Number) r); + if (l instanceof Number lN && r instanceof Number rN) { + return compare(lN, rN); + } + + // automatic conversion for versions + if (l instanceof Version lV && r instanceof String rStr) { + return lV.compareTo(new Version(rStr)); + } + if (l instanceof String lStr && r instanceof Version rV) { + return new Version(lStr).compareTo(rV); } - if (l instanceof Comparable && r instanceof Comparable) { + if (l instanceof Comparable lC && r instanceof Comparable) { try { - return Integer.valueOf(((Comparable) l).compareTo(r)); + return Integer.valueOf(lC.compareTo(r)); } catch (ClassCastException cce) { // when types are not compatible, cce is thrown // fall back to null 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 index f239e6bc8b08c..0a285b9e43d3d 100644 --- 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 @@ -12,11 +12,14 @@ import org.elasticsearch.xpack.ql.type.DataType; import static org.elasticsearch.Version.V_8_2_0; +import static org.elasticsearch.Version.V_8_4_0; import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; +import static org.elasticsearch.xpack.ql.type.DataTypes.VERSION; public final class VersionCompatibilityChecks { public static final Version INTRODUCING_UNSIGNED_LONG = V_8_2_0; + public static final Version INTRODUCING_VERSION_FIELD_TYPE = V_8_4_0; private VersionCompatibilityChecks() {} @@ -24,6 +27,9 @@ public static boolean isTypeSupportedInVersion(DataType dataType, Version versio if (dataType == UNSIGNED_LONG) { return supportsUnsignedLong(version); } + if (dataType == VERSION) { + return supportsVersionType(version); + } return true; } @@ -34,10 +40,20 @@ public static boolean supportsUnsignedLong(Version version) { return INTRODUCING_UNSIGNED_LONG.compareTo(version) <= 0; } + /** + * Does the provided {@code version} support the version type (PR#85502)? + */ + public static boolean supportsVersionType(Version version) { + return INTRODUCING_VERSION_FIELD_TYPE.compareTo(version) <= 0; + } + public static @Nullable Version versionIntroducingType(DataType dataType) { if (dataType == UNSIGNED_LONG) { return INTRODUCING_UNSIGNED_LONG; } + if (dataType == VERSION) { + return INTRODUCING_VERSION_FIELD_TYPE; + } return null; } diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataType.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataType.java index 5b18234347fab..833035fcf49ed 100644 --- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataType.java +++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/DataType.java @@ -7,6 +7,8 @@ package org.elasticsearch.xpack.ql.type; +import org.elasticsearch.search.sort.ScriptSortBuilder; + import java.util.Locale; import java.util.Objects; @@ -62,6 +64,12 @@ public String esType() { return esType; } + public ScriptSortBuilder.ScriptSortType scriptSortType() { + return isNumeric() ? ScriptSortBuilder.ScriptSortType.NUMBER + : this == DataTypes.VERSION ? ScriptSortBuilder.ScriptSortType.VERSION + : ScriptSortBuilder.ScriptSortType.STRING; + } + public boolean isInteger() { return isInteger; } 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 c2319ab8bde9d..02a9f0f05e255 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 @@ -11,6 +11,7 @@ import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.core.Booleans; import org.elasticsearch.xpack.ql.QlIllegalArgumentException; +import org.elasticsearch.xpack.versionfield.Version; import java.io.IOException; import java.math.BigDecimal; @@ -34,6 +35,7 @@ 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.VERSION; 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; @@ -170,6 +172,9 @@ public static Converter converterFor(DataType from, DataType to) { if (to == IP) { return conversionToIp(from); } + if (to == VERSION) { + return conversionToVersion(from); + } return null; } @@ -187,6 +192,13 @@ private static Converter conversionToIp(DataType from) { return null; } + private static Converter conversionToVersion(DataType from) { + if (isString(from)) { + return DefaultConverter.STRING_TO_VERSION; + } + return null; + } + private static Converter conversionToUnsignedLong(DataType from) { if (from.isRational()) { return DefaultConverter.RATIONAL_TO_UNSIGNED_LONG; @@ -537,7 +549,8 @@ public enum DefaultConverter implements Converter { throw new QlIllegalArgumentException("[" + o + "] is not a valid IPv4 or IPv6 address"); } return o; - }); + }), + STRING_TO_VERSION(o -> new Version(o.toString())); public static final String NAME = "dtc-def"; 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 4dc8bf046a90f..8741025c34b17 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 @@ -44,6 +44,8 @@ public final class DataTypes { public static final DataType DATETIME = new DataType("DATETIME", "date", Long.BYTES, false, false, true); // ip public static final DataType IP = new DataType("ip", 45, false, false, true); + // version + public static final DataType VERSION = new DataType("version", Integer.MAX_VALUE, false, false, true); // binary public static final DataType BINARY = new DataType("binary", Integer.MAX_VALUE, false, false, true); // complex types @@ -68,6 +70,7 @@ public final class DataTypes { TEXT, DATETIME, IP, + VERSION, BINARY, OBJECT, NESTED 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 13a1182c1f7f6..3d439acc04776 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 @@ -11,6 +11,7 @@ import org.elasticsearch.xpack.ql.expression.Literal; import org.elasticsearch.xpack.ql.tree.Location; import org.elasticsearch.xpack.ql.tree.Source; +import org.elasticsearch.xpack.versionfield.Version; import java.math.BigDecimal; import java.math.BigInteger; @@ -32,6 +33,7 @@ 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.VERSION; import static org.elasticsearch.xpack.ql.type.DateUtils.asDateTime; import static org.elasticsearch.xpack.ql.util.NumericUtils.UNSIGNED_LONG_MAX; @@ -563,4 +565,25 @@ public void testIpToString() { Converter stringToIp = converterFor(KEYWORD, IP); assertEquals("10.0.0.1", ipToString.convert(stringToIp.convert(new Literal(s, "10.0.0.1", KEYWORD)))); } + + public void testStringToVersion() { + Converter conversion = converterFor(randomFrom(TEXT, KEYWORD), VERSION); + assertNull(conversion.convert(null)); + assertEquals(new Version("2.1.4").toString(), conversion.convert("2.1.4").toString()); + assertEquals(new Version("2.1.4").toBytesRef(), ((Version) conversion.convert("2.1.4")).toBytesRef()); + assertEquals(new Version("2.1.4-SNAPSHOT").toString(), conversion.convert("2.1.4-SNAPSHOT").toString()); + assertEquals(new Version("2.1.4-SNAPSHOT").toBytesRef(), ((Version) conversion.convert("2.1.4-SNAPSHOT")).toBytesRef()); + } + + public void testVersionToString() { + Source s = new Source(Location.EMPTY, "2.1.4"); + Source s2 = new Source(Location.EMPTY, "2.1.4-SNAPSHOT"); + DataType stringType = randomFrom(TEXT, KEYWORD); + Converter versionToString = converterFor(VERSION, stringType); + assertEquals("2.1.4", versionToString.convert(new Literal(s, "2.1.4", VERSION))); + assertEquals("2.1.4-SNAPSHOT", versionToString.convert(new Literal(s2, "2.1.4-SNAPSHOT", VERSION))); + Converter stringToVersion = converterFor(stringType, VERSION); + assertEquals("2.1.4", versionToString.convert(stringToVersion.convert(new Literal(s, "2.1.4", stringType)))); + assertEquals("2.1.4-SNAPSHOT", versionToString.convert(stringToVersion.convert(new Literal(s2, "2.1.4-SNAPSHOT", stringType)))); + } } 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 fa916cebd73fc..d8633532f02f1 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 @@ -186,6 +186,13 @@ public void testIpField() { assertThat(dt.getDataType().typeName(), is("ip")); } + public void testVersionField() { + Map mapping = loadMapping("mapping-version.json"); + assertThat(mapping.size(), is(1)); + EsField dt = mapping.get("version_number"); + assertThat(dt.getDataType().typeName(), is("version")); + } + public void testConstantKeywordField() { Map mapping = loadMapping("mapping-constant-keyword.json"); assertThat(mapping.size(), is(1)); 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 fa73523d7ea27..b5b3d42816502 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 @@ -55,6 +55,7 @@ "properties": { "point": {"type" : "geo_point"} } - } + }, + "version": {"type" : "version"} } } diff --git a/x-pack/plugin/ql/src/test/resources/mapping-version.json b/x-pack/plugin/ql/src/test/resources/mapping-version.json new file mode 100644 index 0000000000000..d258804445843 --- /dev/null +++ b/x-pack/plugin/ql/src/test/resources/mapping-version.json @@ -0,0 +1,7 @@ +{ + "properties" : { + "version_number" : { + "type" : "version" + } + } +} 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 6fd6bb7e83d6e..810d4a3ced8b4 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 @@ -48,7 +48,8 @@ public enum EsType implements SQLType { GEO_POINT(ExtraTypes.GEOMETRY), GEO_SHAPE(ExtraTypes.GEOMETRY), SHAPE(ExtraTypes.GEOMETRY), - UNSIGNED_LONG(Types.NUMERIC); + UNSIGNED_LONG(Types.NUMERIC), + VERSION(Types.VARCHAR); private final Integer type; 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 a0717cb53ada3..80645f963ea40 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 @@ -255,8 +255,8 @@ static Object convert(Object v, EsType columnType, String typeString) throws SQL case GEO_POINT: case GEO_SHAPE: case SHAPE: - return v.toString(); case IP: + case VERSION: return v.toString(); default: throw new SQLException("Unexpected column type [" + typeString + "]"); 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 c3be465e42b7e..c8b7b954e2e0c 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 @@ -93,6 +93,7 @@ private TypeUtils() {} types.put(EsType.BINARY, byte[].class); types.put(EsType.DATETIME, Timestamp.class); types.put(EsType.IP, String.class); + types.put(EsType.VERSION, String.class); types.put(EsType.INTERVAL_YEAR, Period.class); types.put(EsType.INTERVAL_MONTH, Period.class); types.put(EsType.INTERVAL_YEAR_TO_MONTH, Period.class); 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 120081a7edf7f..fb5b69a053f13 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 @@ -27,6 +27,7 @@ import java.util.Map; import static org.elasticsearch.Version.V_8_2_0; +import static org.elasticsearch.Version.V_8_4_0; import static org.elasticsearch.common.time.DateUtils.toMilliSeconds; import static org.elasticsearch.test.ESTestCase.randomLongBetween; @@ -155,4 +156,9 @@ static boolean versionSupportsDateNanos() { public static boolean isUnsignedLongSupported() { return JDBC_DRIVER_VERSION.onOrAfter(V_8_2_0); } + + public static boolean isVersionFieldTypeSupported() { + return JDBC_DRIVER_VERSION.onOrAfter(V_8_4_0); + } + } 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 cbeeee12262ca..fae53e6d214a6 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 @@ -79,6 +79,7 @@ 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.isVersionFieldTypeSupported; 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; @@ -2244,6 +2245,25 @@ public void testResultSetConsumed() throws IOException { assertEquals("No row available", sqle.getMessage()); } + public void testSingleVersionFieldValue() throws SQLException, IOException { + assumeTrue("Driver version [" + JDBC_DRIVER_VERSION + "] doesn't support VERSION fields", isVersionFieldTypeSupported()); + + createTestDataForVersionType(); + String query = "SELECT name, version FROM test WHERE version = '1.3.0' OR version = 'foo' ORDER BY version ASC"; + doWithQuery(query, results -> { + assertTrue(results.next()); + assertEquals("version 1.3.0", results.getString("name")); + assertEquals("1.3.0", results.getString("version")); + SQLException sqle = expectThrows(SQLException.class, () -> results.getByte("version")); + assertEquals(format(Locale.ROOT, "Unable to convert value [%.128s] of type [VERSION] to [Byte]", "1.3.0"), sqle.getMessage()); + assertTrue(results.next()); + assertEquals("version foo", results.getString("name")); + assertEquals("foo", results.getString("version")); + assertFalse(results.next()); + }); + + } + private void doWithQuery(String query, CheckedConsumer consumer) throws SQLException { doWithQuery(() -> esJdbc(timeZoneId), query, consumer); } @@ -2470,6 +2490,48 @@ private static void indexTestFieldsDoc(String docId, Object... values) throws IO }); } + private void createTestDataForVersionType() throws IOException { + createIndexWithMapping("test"); + updateMapping("test", builder -> { + builder.startObject("name").field("type", "keyword").endObject(); + builder.startObject("version").field("type", "version").endObject(); + }); + + int docId = 1; + // first two deterministic values + index("test", "" + (docId++), builder -> { + builder.array("version", "1.3.0"); + builder.array("name", "version 1.3.0"); + }); + index("test", "" + (docId++), builder -> { + builder.array("version", "1.11.0"); + builder.array("name", "version 1.11.0"); + }); + + // some higher versions + for (int i = 0; i < randomInt(10); i++) { + index("test", "" + (docId++), builder -> { + String versionVal = (2 + randomInt(50)) + "." + randomInt(50) + "." + randomInt(50); + builder.array("version", versionVal); + builder.array("name", "version " + versionVal); + }); + } + + // some invalid versions + index("test", "" + (docId++), builder -> { + String versionVal = "foo"; + builder.array("version", versionVal); + builder.array("name", "version " + versionVal); + }); + for (int i = 0; i < randomInt(10); i++) { + index("test", "" + (docId++), builder -> { + String versionVal = "foo" + randomInt(1000); + builder.array("version", versionVal); + builder.array("name", "version " + versionVal); + }); + } + } + /** * 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. diff --git a/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlSearchIT.java b/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlSearchIT.java index e7c2825c578a4..f09875a660b98 100644 --- a/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlSearchIT.java +++ b/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/java/org/elasticsearch/xpack/sql/qa/mixed_node/SqlSearchIT.java @@ -40,7 +40,7 @@ import static org.elasticsearch.xpack.ql.TestUtils.readResource; public class SqlSearchIT extends ESRestTestCase { - + private static final Version VERSION_FIELD_QL_INTRODUCTION = Version.V_8_4_0; private static final String index = "test_sql_mixed_versions"; private static int numShards; private static int numReplicas = 1; @@ -153,6 +153,9 @@ private Map prepareTestData( columns.add(columnInfo("scaled_float_field", "scaled_float")); columns.add(columnInfo("boolean_field", "boolean")); columns.add(columnInfo("ip_field", "ip")); + if (bwcVersion.onOrAfter(VERSION_FIELD_QL_INTRODUCTION)) { + columns.add(columnInfo("version_field", "version")); + } columns.add(columnInfo("text_field", "text")); columns.add(columnInfo("keyword_field", "keyword")); columns.add(columnInfo("constant_keyword_field", "keyword")); @@ -184,6 +187,9 @@ private Map prepareTestData( builder.append("\"scaled_float_field\":" + fieldValues.computeIfAbsent("scaled_float_field", v -> 123.5d) + ","); builder.append("\"boolean_field\":" + fieldValues.computeIfAbsent("boolean_field", v -> randomBoolean()) + ","); builder.append("\"ip_field\":\"" + fieldValues.computeIfAbsent("ip_field", v -> "123.123.123.123") + "\","); + if (bwcVersion.onOrAfter(VERSION_FIELD_QL_INTRODUCTION)) { + columns.add(columnInfo("version_field", "2.11.4")); + } builder.append("\"text_field\": \"" + fieldValues.computeIfAbsent("text_field", v -> randomAlphaOfLength(5)) + "\","); builder.append("\"keyword_field\": \"" + fieldValues.computeIfAbsent("keyword_field", v -> randomAlphaOfLength(5)) + "\","); builder.append( diff --git a/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/resources/all_field_types.json b/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/resources/all_field_types.json index 491be6f0f96b6..3ac55d06e4d4e 100644 --- a/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/resources/all_field_types.json +++ b/x-pack/plugin/sql/qa/mixed-node/src/javaRestTest/resources/all_field_types.json @@ -30,6 +30,9 @@ "ip_field": { "type": "ip" }, + "version_field": { + "type": "version" + }, "text_field": { "type": "text" }, @@ -56,4 +59,4 @@ "shape_field": { "type": "shape" } -} \ No newline at end of file +} 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 e2c7fda3fab33..d3841c79b46cc 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 @@ -358,6 +358,25 @@ public void testIpField() throws IOException { assertResponse(expected, runSql(query)); } + /* + * "version_field": { + * "type": "version", + * } + */ + public void testVersionField() throws IOException { + String query = "SELECT version_field FROM test"; + String actualValue = "2.11.4"; + + Map> fieldProps = null; + createIndexWithFieldTypeAndProperties("version", fieldProps, getIndexProps()); + index("{\"version_field\":\"" + actualValue + "\"}"); + + Map expected = new HashMap<>(); + expected.put("columns", asList(columnInfo("plain", "version_field", "version", JDBCType.VARCHAR, Integer.MAX_VALUE))); + expected.put("rows", singletonList(singletonList(getExpectedValueFromSource(actualValue)))); + assertResponse(expected, runSql(query)); + } + /* * "geo_point_field": { * "type": "geo_point", 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 0811b4d9a448e..88fbf4112c611 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 @@ -116,6 +116,11 @@ public void testIPs() throws IOException { ); } + public void testVersionFields() throws IOException { + assertQuery("SELECT CAST('1.2.3' AS VERSION)", "CAST('1.2.3' AS VERSION)", "version", "1.2.3", 2147483647); + assertQuery("SELECT CAST('bad' AS VERSION)", "CAST('bad' AS VERSION)", "version", "bad", 2147483647); + } + public void testDateTimeIntervals() throws IOException { assertQuery("SELECT INTERVAL '326' YEAR", "INTERVAL '326' YEAR", "interval_year", "P326Y", "+326-0", 7); assertQuery("SELECT INTERVAL '50' MONTH", "INTERVAL '50' MONTH", "interval_month", "P50M", "+0-50", 7); 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 eac026689f72f..cb0a8692ba34e 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 @@ -65,6 +65,7 @@ public static void createEmptyIndex(RestClient client, String index) throws Exce protected static void loadEmpDatasetIntoEs(RestClient client) throws Exception { loadEmpDatasetIntoEs(client, "test_emp", "employees"); loadEmpDatasetWithExtraIntoEs(client, "test_emp_copy", "employees"); + loadAppsDatasetIntoEs(client, "apps", "apps"); loadLogsDatasetIntoEs(client, "logs", "logs"); loadLogNanosDatasetIntoEs(client, "logs_nanos", "logs_nanos"); loadLogUnsignedLongIntoEs(client, "logs_unsigned_long", "logs_unsigned_long"); @@ -274,6 +275,41 @@ private static void loadEmpDatasetIntoEs(RestClient client, String index, String client.performRequest(request); } + protected static void loadAppsDatasetIntoEs(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("version"); + { + createIndex.field("type", "version"); + createIndex.startObject("fields"); + { + createIndex.startObject("raw").field("type", "keyword").endObject(); + } + createIndex.endObject(); + } + createIndex.endObject(); + createIndex.startObject("name"); + { + createIndex.field("type", "text"); + createIndex.startObject("fields"); + { + createIndex.startObject("raw").field("type", "keyword").endObject(); + } + createIndex.endObject(); + } + createIndex.endObject(); + } + createIndex.endObject(); + } + createIndex.endObject(); + + loadDatasetIntoEs(client, index, filename, createIndex); + } + protected static void loadLogsDatasetIntoEs(RestClient client, String index, String filename) throws Exception { XContentBuilder createIndex = JsonXContent.contentBuilder().startObject(); createIndex.startObject("mappings"); diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/apps.csv b/x-pack/plugin/sql/qa/server/src/main/resources/apps.csv new file mode 100644 index 0000000000000..9bd4123424f02 --- /dev/null +++ b/x-pack/plugin/sql/qa/server/src/main/resources/apps.csv @@ -0,0 +1,15 @@ +id,version,name +1,1,aaaaa +2,2.1,bbbbb +3,2.3.4,ccccc +4,2.12.0,ddddd +5,1.11.0,eeeee +6,5.2.9,fffff +7,5.2.9-SNAPSHOT,ggggg +8,1.2.3.4,hhhhh +9,bad,iiiii +10,5.2.9,jjjjj +11,,kkkkk +12,1.2.3.4,aaaaa +13,,lllll +14,5.2.9,mmmmm 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 785cc0f26ad1f..178d4fd2128e0 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 @@ -232,6 +232,7 @@ showTables SHOW TABLES; catalog | name | type | kind +javaRestTest |apps |TABLE |INDEX javaRestTest |empty_mapping |TABLE |INDEX javaRestTest |logs |TABLE |INDEX javaRestTest |logs_nanos |TABLE |INDEX @@ -283,6 +284,7 @@ showTablesLocalCatalog SHOW TABLES CATALOG 'javaRestTest'; catalog | name | type | kind +javaRestTest |apps |TABLE |INDEX javaRestTest |empty_mapping |TABLE |INDEX javaRestTest |logs |TABLE |INDEX javaRestTest |logs_nanos |TABLE |INDEX 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 9b3fccd87c720..b77b82d198392 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 @@ -121,6 +121,11 @@ 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 |apps |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |1 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |apps |name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |2 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |apps |name.raw |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |3 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |apps |version |12 |VERSION |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |null |4 |YES |null |null |null |null |NO |NO +my_remote_cluster|null |apps |version.raw |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO 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 @@ -172,6 +177,7 @@ 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 |apps |TABLE | |null |null |null |null |null 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 @@ -201,6 +207,7 @@ 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 |apps |TABLE | |null |null |null |null |null 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 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 3815a2a02fd00..be615e6d7df9f 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 @@ -23,6 +23,7 @@ SHOW TABLES CATALOG 'my_remote_cluster'; catalog | name | type | kind -----------------+-------------------+---------------+--------------- +my_remote_cluster|apps |TABLE |INDEX my_remote_cluster|empty_mapping |TABLE |INDEX my_remote_cluster|logs |TABLE |INDEX my_remote_cluster|logs_nanos |TABLE |INDEX @@ -36,6 +37,7 @@ SHOW TABLES CATALOG LIKE 'my_remote_%'; catalog | name | type | kind -----------------+-------------------+---------------+--------------- +my_remote_cluster|apps |TABLE |INDEX my_remote_cluster|empty_mapping |TABLE |INDEX my_remote_cluster|logs |TABLE |INDEX my_remote_cluster|logs_nanos |TABLE |INDEX @@ -49,6 +51,7 @@ SHOW TABLES CATALOG 'my_remote_cluster' INCLUDE FROZEN; catalog | name | type | kind -----------------+-------------------+---------------+--------------- +my_remote_cluster|apps |TABLE |INDEX my_remote_cluster|empty_mapping |TABLE |INDEX my_remote_cluster|frozen_emp |TABLE |INDEX my_remote_cluster|logs |TABLE |INDEX @@ -119,6 +122,7 @@ SHOW TABLES CATALOG '*'; catalog | name | type | kind -----------------+-------------------+---------------+--------------- +my_remote_cluster|apps |TABLE |INDEX my_remote_cluster|empty_mapping |TABLE |INDEX my_remote_cluster|logs |TABLE |INDEX my_remote_cluster|logs_nanos |TABLE |INDEX 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 5c70505cc80b7..72fc63590bace 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 @@ -100,8 +100,13 @@ javaRestTest |null |test_alias |wildcard_name |12 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 ------------------+---------------+-------------------+------------------+---------------+----------------+---------------+---------------+----------------+---------------+---------------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ + 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 +--------------------+---------------+-------------------+------------------+---------------+----------------+--------------+---------------+---------------+---------------+---------------+---------------+---------------+---------------+----------------+-----------------+----------------+---------------+---------------+---------------+---------------+----------------+----------------+------------------ +javaRestTest |null |apps |id |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |1 |YES |null |null |null |null |NO |NO +javaRestTest |null |apps |name |12 |TEXT |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |2 |YES |null |null |null |null |NO |NO +javaRestTest |null |apps |name.raw |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |3 |YES |null |null |null |null |NO |NO +javaRestTest |null |apps |version |12 |VERSION |2147483647 |2147483647 |null |null |1 |null |null |12 |0 |null |4 |YES |null |null |null |null |NO |NO +javaRestTest |null |apps |version.raw |12 |KEYWORD |32766 |2147483647 |null |null |1 |null |null |12 |0 |2147483647 |5 |YES |null |null |null |null |NO |NO javaRestTest |null |logs |@timestamp |93 |DATETIME |34 |8 |null |null |1 |null |null |9 |3 |null |1 |YES |null |null |null |null |NO |NO javaRestTest |null |logs |bytes_in |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |2 |YES |null |null |null |null |NO |NO javaRestTest |null |logs |bytes_out |4 |INTEGER |11 |4 |null |10 |1 |null |null |4 |0 |null |3 |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 c8603e3c1030c..289af43d9e8c2 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 @@ -7,7 +7,8 @@ showTables SHOW TABLES INCLUDE FROZEN; - catalog | name | type | kind + catalog | name | type | kind +javaRestTest |apps |TABLE |INDEX javaRestTest |empty_mapping |TABLE |INDEX javaRestTest |frozen_emp |TABLE |FROZEN INDEX javaRestTest |logs |TABLE |INDEX diff --git a/x-pack/plugin/sql/qa/server/src/main/resources/version.csv-spec b/x-pack/plugin/sql/qa/server/src/main/resources/version.csv-spec new file mode 100644 index 0000000000000..0276fd3a2b185 --- /dev/null +++ b/x-pack/plugin/sql/qa/server/src/main/resources/version.csv-spec @@ -0,0 +1,421 @@ +// To mute tests follow example in file: example.csv-spec + +// +// Tests for VERSION fields +// + +selectAll +SELECT * FROM apps ORDER BY id; + + id:i | name:s | version:s +------+-----------+--------- +1 |aaaaa |1 +2 |bbbbb |2.1 +3 |ccccc |2.3.4 +4 |ddddd |2.12.0 +5 |eeeee |1.11.0 +6 |fffff |5.2.9 +7 |ggggg |5.2.9-SNAPSHOT +8 |hhhhh |1.2.3.4 +9 |iiiii |bad +10 |jjjjj |5.2.9 +11 |kkkkk |null +12 |aaaaa |1.2.3.4 +13 |lllll |null +14 |mmmmm |5.2.9 + +; + +filterByVersion +SELECT * FROM apps WHERE version = '2.12.0'; + + id | name | version +------+-----------+--------- +4 |ddddd |2.12.0 +; + +keywordSubfield +SELECT version.raw as k FROM apps WHERE version = '2.12.0'; + + k +--------- + 2.12.0 +; + + +projectionVersion +SELECT version FROM apps WHERE id = 3; + + version +--------- +2.3.4 +; + + +versionRange1 +SELECT * FROM apps WHERE version > '2.2' ORDER BY id; + + id | name | version +------+-----------+--------- +3 |ccccc |2.3.4 +4 |ddddd |2.12.0 +6 |fffff |5.2.9 +7 |ggggg |5.2.9-SNAPSHOT +9 |iiiii |bad +10 |jjjjj |5.2.9 +14 |mmmmm |5.2.9 + +; + +versionRange2 +SELECT * FROM apps WHERE version >= '2.3.4' ORDER BY id; + + id | name | version +------+-----------+--------------- +3 |ccccc |2.3.4 +4 |ddddd |2.12.0 +6 |fffff |5.2.9 +7 |ggggg |5.2.9-SNAPSHOT +9 |iiiii |bad +10 |jjjjj |5.2.9 +14 |mmmmm |5.2.9 + +; + + +between +SELECT * FROM apps WHERE version BETWEEN '1.10' AND '5.2.9' ORDER BY id; + + id:i | name:s | version:s +--------+-------------+--------------- +2 |bbbbb | 2.1 +3 |ccccc | 2.3.4 +4 |ddddd | 2.12.0 +5 |eeeee | 1.11.0 +6 |fffff | 5.2.9 +7 |ggggg | 5.2.9-SNAPSHOT +10 |jjjjj | 5.2.9 +14 |mmmmm | 5.2.9 + +; + +rlike +SELECT * FROM apps WHERE version::string RLIKE '2.*.4'; + + id:i | name:s | version:s +--------+-------------+--------------- +3 |ccccc | 2.3.4 + +; + +orderByVersion +SELECT * FROM apps ORDER BY version, id; + + id:i | name:s | version:s +------+-----------+--------------- +1 |aaaaa |1 +8 |hhhhh |1.2.3.4 +12 |aaaaa |1.2.3.4 +5 |eeeee |1.11.0 +2 |bbbbb |2.1 +3 |ccccc |2.3.4 +4 |ddddd |2.12.0 +7 |ggggg |5.2.9-SNAPSHOT +6 |fffff |5.2.9 +10 |jjjjj |5.2.9 +14 |mmmmm |5.2.9 +9 |iiiii |bad +11 |kkkkk |null +13 |lllll |null + +; + +orderByVersionDesc +SELECT * FROM apps ORDER BY version DESC, id ASC; + + id:i | name:s | version:s +------+-----------+--------------- +11 |kkkkk |null +13 |lllll |null +9 |iiiii |bad +6 |fffff |5.2.9 +10 |jjjjj |5.2.9 +14 |mmmmm |5.2.9 +7 |ggggg |5.2.9-SNAPSHOT +4 |ddddd |2.12.0 +3 |ccccc |2.3.4 +2 |bbbbb |2.1 +5 |eeeee |1.11.0 +8 |hhhhh |1.2.3.4 +12 |aaaaa |1.2.3.4 +1 |aaaaa |1 + +; + + +orderByVersionNullsFirst +SELECT * FROM apps ORDER BY version NULLS FIRST, id; + + id:i | name:s | version:s +------+-----------+--------------- +11 |kkkkk |null +13 |lllll |null +1 |aaaaa |1 +8 |hhhhh |1.2.3.4 +12 |aaaaa |1.2.3.4 +5 |eeeee |1.11.0 +2 |bbbbb |2.1 +3 |ccccc |2.3.4 +4 |ddddd |2.12.0 +7 |ggggg |5.2.9-SNAPSHOT +6 |fffff |5.2.9 +10 |jjjjj |5.2.9 +14 |mmmmm |5.2.9 +9 |iiiii |bad + +; + + +orderByVersionScript +SELECT * FROM apps ORDER BY CONCAT('1.', CAST(version AS TEXT))::version, id; + + id:i | name:s | version:s +------+-----------+--------------- +1 |aaaaa |1 +8 |hhhhh |1.2.3.4 +12 |aaaaa |1.2.3.4 +5 |eeeee |1.11.0 +2 |bbbbb |2.1 +3 |ccccc |2.3.4 +4 |ddddd |2.12.0 +7 |ggggg |5.2.9-SNAPSHOT +6 |fffff |5.2.9 +10 |jjjjj |5.2.9 +14 |mmmmm |5.2.9 +11 |kkkkk |null +13 |lllll |null +9 |iiiii |bad + +; + +countVersion +SELECT count(version) as v, name.raw as k FROM apps GROUP by k ORDER BY k; + +v:l | k:s +--------+-------- +2 | aaaaa +1 | bbbbb +1 | ccccc +1 | ddddd +1 | eeeee +1 | fffff +1 | ggggg +1 | hhhhh +1 | iiiii +1 | jjjjj +0 | kkkkk +0 | lllll +1 | mmmmm + +; + +groupByVersion +SELECT count(*) as c, max(id) as maxid, version FROM apps GROUP BY version ORDER BY version; + +c:l | maxid:i | version:s +----+---------+-----------------+ +2 |13 |null +1 |1 |1 +2 |12 |1.2.3.4 +1 |5 |1.11.0 +1 |2 |2.1 +1 |3 |2.3.4 +1 |4 |2.12.0 +1 |7 |5.2.9-SNAPSHOT +3 |14 |5.2.9 +1 |9 |bad + +; + +groupOrderLimit +SELECT version, version as v, version as v2 FROM apps WHERE version IS NOT NULL GROUP BY 1 ORDER BY 2 DESC LIMIT 3; + + version:s | v:s | v2:s +------------------+------------------+------------------ + bad | bad | bad + 5.2.9 | 5.2.9 | 5.2.9 + 5.2.9-SNAPSHOT | 5.2.9-SNAPSHOT | 5.2.9-SNAPSHOT + +; + +groupByVersionScript +SELECT max(id) as id FROM apps GROUP BY CONCAT('1.', CAST(version AS TEXT))::version ORDER BY id; + + id:i +------ +1 +2 +3 +4 +5 +7 +9 +12 +13 +14 +; + +castToVersion +SELECT id, name, CONCAT('1.', CAST(version AS TEXT))::version version FROM apps ORDER BY id; + + id:i | name:s | version:s +------+-----------+--------------- +1 |aaaaa |1.1 +2 |bbbbb |1.2.1 +3 |ccccc |1.2.3.4 +4 |ddddd |1.2.12.0 +5 |eeeee |1.1.11.0 +6 |fffff |1.5.2.9 +7 |ggggg |1.5.2.9-SNAPSHOT +8 |hhhhh |1.1.2.3.4 +9 |iiiii |1.bad +10 |jjjjj |1.5.2.9 +11 |kkkkk |1. +12 |aaaaa |1.1.2.3.4 +13 |lllll |1. +14 |mmmmm |1.5.2.9 + +; + + +castConstantToVersion +SELECT '1.2.3'::version as v; + + v +------ +1.2.3 + +; + + +castConstantToVersion2 +select '1.2.3'::version v from apps; + + v +------ +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 + +; + + +multipleCast +select '1.2.3'::version::string v from apps; + + v +------ +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 +1.2.3 + +; + + +compareVersions +SELECT '1.2.3'::version < '1.11.4'::version as v; + + v +------ +true + +; + + +filterByVersionConstant +SELECT * FROM apps WHERE '1.2.0'::version < '1.11.0'::version ORDER BY id LIMIT 1; + + id:i | name:s | version:s +------+-----------+--------- +1 |aaaaa |1 + +; + +filterByVersionConstant2 +SELECT * FROM apps WHERE '1.2.0'::version < '1.11.0' ORDER BY id LIMIT 1; + + id:i | name:s | version:s +------+-----------+--------- +1 |aaaaa |1 + +; + + +filterByVersionConstant3 +SELECT * FROM apps WHERE '1.2.0' < '1.11.0'::version ORDER BY id LIMIT 1; + + id:i | name:s | version:s +------+-----------+--------- +1 |aaaaa |1 + +; + +groupByVersionHaving +SELECT max(id) as idx, version FROM apps GROUP BY version HAVING idx = 14; + + idx:i | version:s +-------+----------- +14 | 5.2.9 + +; + +scriptsOperatorsOrderBy +SELECT CONCAT('123',CAST(version AS TEXT))::version v, version, CAST(version AS TEXT) version_text, id, +IIF(version > '1.1', 1, 0) m, GREATEST(version, '1.3.0'::version) g, IFNULL(version, '0.1'::version) i, +CASE WHEN version > '1.1' THEN 'high' + WHEN version IS NULL THEN 'none' + ELSE 'low' +END AS c +FROM apps WHERE version IS NULL OR version_text LIKE '1%' ORDER BY version DESC NULLS LAST, id DESC; + + v:s | version:s | version_text:s | id:i | m:i | g:s | i:s | c:s +------------+-----------+-----------------+-------+-------+--------+---------+--------- + 1231.11.0 | 1.11.0 | 1.11.0 | 5 | 1 | 1.11.0 | 1.11.0 | high + 1231.2.3.4 | 1.2.3.4 | 1.2.3.4 | 12 | 1 | 1.3.0 | 1.2.3.4 | high + 1231.2.3.4 | 1.2.3.4 | 1.2.3.4 | 8 | 1 | 1.3.0 | 1.2.3.4 | high + 1231 | 1 | 1 | 1 | 0 | 1.3.0 | 1 | low + 123 | null | null | 13 | 0 | 1.3.0 | 0.1 | none + 123 | null | null | 11 | 0 | 1.3.0 | 0.1 | none + +; + +selectFirstLastVersion +select first(version) as first, last(version) as last from apps; + +first:s | last:s +---------+------------ +1 | bad + +; diff --git a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java index 2f52d5aefff13..7119953829368 100644 --- a/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java +++ b/x-pack/plugin/sql/sql-action/src/main/java/org/elasticsearch/xpack/sql/action/SqlQueryResponse.java @@ -285,6 +285,8 @@ public static XContentBuilder value(XContentBuilder builder, Mode mode, SqlVersi // use the SQL format for intervals when sending back the response for CLI // all other clients will receive ISO 8601 formatted intervals builder.value(value.toString()); + } else if (value instanceof org.elasticsearch.xpack.versionfield.Version) { + builder.value(value.toString()); } else { builder.value(value); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/SourceGenerator.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/SourceGenerator.java index ae216a903f5f2..b9ebe75fd2879 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/SourceGenerator.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/execution/search/SourceGenerator.java @@ -13,7 +13,6 @@ import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.NestedSortBuilder; -import org.elasticsearch.search.sort.ScriptSortBuilder.ScriptSortType; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.xpack.ql.execution.search.QlSourceBuilder; import org.elasticsearch.xpack.ql.expression.Attribute; @@ -142,10 +141,7 @@ private static void sorting(QueryContainer container, SearchSourceBuilder source } } } else if (sortable instanceof ScriptSort ss) { - sortBuilder = scriptSort( - ss.script().toPainless(), - ss.script().outputType().isNumeric() ? ScriptSortType.NUMBER : ScriptSortType.STRING - ); + sortBuilder = scriptSort(ss.script().toPainless(), ss.script().outputType().scriptSortType()); } else if (sortable instanceof ScoreSort) { sortBuilder = scoreSort(); } 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 54187aa0ffde2..67b430d5ba141 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 @@ -27,6 +27,7 @@ import static org.elasticsearch.xpack.ql.type.DataTypes.DATETIME; import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; +import static org.elasticsearch.xpack.ql.type.DataTypes.VERSION; 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; @@ -133,6 +134,9 @@ protected Object unwrapCustomValue(Object values) { // since its later processing will be type dependent. (ex.: negation of UL is only "safe" for 0 values) return convert(values, UNSIGNED_LONG); } + if (dataType == VERSION) { + return convert(values, VERSION); + } return null; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPainlessExtension.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPainlessExtension.java index 33a85e158647b..b17bcbf76f77f 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPainlessExtension.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPainlessExtension.java @@ -11,6 +11,7 @@ import org.elasticsearch.painless.spi.WhitelistLoader; import org.elasticsearch.script.AggregationScript; import org.elasticsearch.script.BucketAggregationSelectorScript; +import org.elasticsearch.script.BytesRefSortScript; import org.elasticsearch.script.FieldScript; import org.elasticsearch.script.FilterScript; import org.elasticsearch.script.NumberSortScript; @@ -36,6 +37,7 @@ public Map, List> getContextWhitelists() { whitelist.put(FieldScript.CONTEXT, list); whitelist.put(NumberSortScript.CONTEXT, list); whitelist.put(StringSortScript.CONTEXT, list); + whitelist.put(BytesRefSortScript.CONTEXT, list); whitelist.put(BucketAggregationSelectorScript.CONTEXT, list); return whitelist; } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/TopHitsAgg.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/TopHitsAgg.java index 6de81fac8f650..e87cff0c06397 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/TopHitsAgg.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/TopHitsAgg.java @@ -60,9 +60,7 @@ AggregationBuilder toBuilder() { sortBuilderList.add( new ScriptSortBuilder( Scripts.nullSafeSort(sortSource.script()).toPainless(), - sortSource.script().outputType().isNumeric() - ? ScriptSortBuilder.ScriptSortType.NUMBER - : ScriptSortBuilder.ScriptSortType.STRING + source().script().outputType().scriptSortType() ).order(sortOrder) ); } @@ -74,12 +72,8 @@ AggregationBuilder toBuilder() { ); } else { sortBuilderList.add( - new ScriptSortBuilder( - Scripts.nullSafeSort(source().script()).toPainless(), - source().script().outputType().isNumeric() - ? ScriptSortBuilder.ScriptSortType.NUMBER - : ScriptSortBuilder.ScriptSortType.STRING - ).order(sortOrder) + new ScriptSortBuilder(Scripts.nullSafeSort(source().script()).toPainless(), source().script().outputType().scriptSortType()) + .order(sortOrder) ); } 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 08467e33af91e..061cdc41a17ae 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 @@ -12,6 +12,7 @@ import org.elasticsearch.xpack.sql.expression.literal.geo.GeoShape; import org.elasticsearch.xpack.sql.expression.literal.interval.Interval; import org.elasticsearch.xpack.sql.expression.literal.interval.Intervals; +import org.elasticsearch.xpack.versionfield.Version; import java.sql.JDBCType; import java.sql.SQLType; @@ -47,6 +48,7 @@ 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.VERSION; import static org.elasticsearch.xpack.ql.type.DataTypes.isDateTime; import static org.elasticsearch.xpack.ql.util.CollectionUtils.mapSize; @@ -229,6 +231,9 @@ public static DataType fromJava(Object value) { if (value instanceof GeoShape) { return GEO_SHAPE; } + if (value instanceof Version) { + return VERSION; + } return null; } @@ -362,6 +367,9 @@ public static SQLType sqlType(DataType dataType) { if (dataType == IP) { return JDBCType.VARCHAR; } + if (dataType == VERSION) { + return JDBCType.VARCHAR; + } if (dataType == BINARY) { return JDBCType.BINARY; } @@ -488,6 +496,9 @@ public static int defaultPrecision(DataType dataType) { if (dataType == IP) { return dataType.size(); } + if (dataType == VERSION) { + return dataType.size(); + } if (dataType == BINARY) { return dataType.size(); } @@ -608,6 +619,9 @@ public static int displaySize(DataType dataType) { if (dataType == IP) { return dataType.size(); } + if (dataType == VERSION) { + return dataType.size(); + } if (dataType == BINARY) { return dataType.size(); } 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 135b20c2589e1..fa7151e1f8751 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 @@ -42,11 +42,13 @@ import java.util.stream.Collectors; import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.INTRODUCING_UNSIGNED_LONG; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.INTRODUCING_VERSION_FIELD_TYPE; 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.ql.type.DataTypes.VERSION; import static org.elasticsearch.xpack.sql.types.SqlTypesTests.loadMapping; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.Matchers.contains; @@ -182,7 +184,7 @@ public void testDottedFieldPathTypo() { public void testStarExpansionExcludesObjectAndUnsupportedTypes() { LogicalPlan plan = plan("SELECT * FROM test"); List list = ((Project) plan).projections(); - assertThat(list, hasSize(13)); + assertThat(list, hasSize(14)); List names = Expressions.names(list); assertThat(names, not(hasItem("some"))); assertThat(names, not(hasItem("some.dotted"))); @@ -349,6 +351,45 @@ public void testUnsignedLongVersionCompatibility() { } } + public void testVersionTypeVersionCompatibility() { + String query = "SELECT version_number FROM test"; + String queryWithCastLiteral = "SELECT '1.2.3'::version AS version_number"; + String queryWithAlias = "SELECT version_number AS version_number FROM test"; + String queryWithCast = "SELECT CONCAT(version_number::string, '-SNAPSHOT')::version AS version_number FROM test"; + + Version preVersion = Version.fromId(INTRODUCING_VERSION_FIELD_TYPE.id - SqlVersion.MINOR_MULTIPLIER); + Version postVersion = Version.fromId(INTRODUCING_VERSION_FIELD_TYPE.id + SqlVersion.MINOR_MULTIPLIER); + SqlConfiguration sqlConfig = SqlTestUtils.randomConfiguration(SqlVersion.fromId(preVersion.id)); + + for (String sql : List.of(query, queryWithCastLiteral, queryWithAlias, queryWithCast)) { + analyzer = new Analyzer( + sqlConfig, + functionRegistry, + loadCompatibleIndexResolution("mapping-version.json", preVersion), + new Verifier(new Metrics()) + ); + VerificationException ex = expectThrows(VerificationException.class, () -> plan(sql)); + assertThat(ex.getMessage(), containsString("Cannot use field [version_number]")); + + for (Version v : List.of(INTRODUCING_VERSION_FIELD_TYPE, postVersion)) { + analyzer = new Analyzer( + SqlTestUtils.randomConfiguration(SqlVersion.fromId(v.id)), + functionRegistry, + loadCompatibleIndexResolution("mapping-version.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(VERSION)); + assertThat(attribute.name(), is("version_number")); + } + } + } + public void testNonProjectedUnsignedLongVersionCompatibility() { Version preUnsignedLong = Version.fromId(INTRODUCING_UNSIGNED_LONG.id - SqlVersion.MINOR_MULTIPLIER); SqlConfiguration sqlConfig = SqlTestUtils.randomConfiguration(SqlVersion.fromId(preUnsignedLong.id)); 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 9edb79211cec9..15615c70b0442 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 @@ -17,6 +17,7 @@ import org.elasticsearch.xpack.sql.proto.StringUtils; import org.elasticsearch.xpack.sql.type.SqlDataTypes; import org.elasticsearch.xpack.sql.util.DateUtils; +import org.elasticsearch.xpack.versionfield.Version; import java.math.BigDecimal; import java.math.BigInteger; @@ -35,6 +36,7 @@ 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.ql.type.DataTypes.VERSION; 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; @@ -220,6 +222,19 @@ public void testUnsignedLongExtraction() { assertEquals(bi, fe.extract(hit)); } + public void testVersionExtraction() { + Version version = new Version(randomAlphaOfLength(10)); + + Object value = randomBoolean() ? version.toString() : version; + + 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, VERSION, randomZone(), randomBoolean()); + + assertEquals(version.toString(), fe.extract(hit).toString()); + } + 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/plan/logical/command/ShowColumnsTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/ShowColumnsTests.java index 2efd00f0e111d..8e0edc54e6fbf 100644 --- 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 @@ -20,6 +20,7 @@ import static java.util.Arrays.asList; import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.supportsUnsignedLong; +import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.supportsVersionType; 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; @@ -30,7 +31,9 @@ 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.VERSION; import static org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumnsTests.UNSIGNED_LONG_TEST_VERSIONS; +import static org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumnsTests.VERSION_FIELD_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; @@ -68,7 +71,8 @@ public void testShowColumns() { 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()) + asList("nested.point", JDBC_TYPE_GEOMETRY, GEO_POINT.typeName()), + asList("version", JDBCType.VARCHAR.getName(), VERSION.typeName()) ); assertEquals(expect.size(), rows.size()); @@ -94,4 +98,16 @@ public void testUnsignedLongFiltering() { assertTrue((supportsUnsignedLong(Version.fromId(version.id)) && rows.contains(rowSupported)) || rows.contains(rowUnsupported)); } } + + public void testVersionFieldFiltering() { + List rowSupported = List.of("version", "VARCHAR", "version"); + List rowUnsupported = List.of("version", "OTHER", "unsupported"); + for (SqlVersion version : VERSION_FIELD_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((supportsVersionType(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 c48af11738bca..fe903ff455710 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 @@ -46,8 +46,10 @@ 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.INTRODUCING_VERSION_FIELD_TYPE; import static org.elasticsearch.xpack.ql.index.VersionCompatibilityChecks.isTypeSupportedInVersion; import static org.elasticsearch.xpack.ql.type.DataTypes.UNSIGNED_LONG; +import static org.elasticsearch.xpack.ql.type.DataTypes.VERSION; import static org.elasticsearch.xpack.sql.proto.Mode.isDriver; import static org.elasticsearch.xpack.sql.types.SqlTypesTests.loadMapping; import static org.mockito.ArgumentMatchers.any; @@ -65,11 +67,18 @@ public class SysColumnsTests extends ESTestCase { SqlVersion.fromId(Version.CURRENT.id) ); + public static List VERSION_FIELD_TEST_VERSIONS = List.of( + SqlVersion.fromId(INTRODUCING_VERSION_FIELD_TYPE.id - SqlVersion.MINOR_MULTIPLIER), + SqlVersion.fromId(INTRODUCING_VERSION_FIELD_TYPE.id), + SqlVersion.fromId(INTRODUCING_VERSION_FIELD_TYPE.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 = 20; - private static final int FIELD_COUNT2 = 18; + private static final int FIELD_COUNT2 = 19; private final SqlParser parser = new SqlParser(); @@ -170,6 +179,30 @@ public void testUnsignedLongFiltering() { } } + public void testVersionTypeFiltering() { + for (Mode mode : List.of(Mode.JDBC, Mode.ODBC)) { + for (SqlVersion version : VERSION_FIELD_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(VERSION, Version.fromId(version.id)), + types.contains(VERSION.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 c88f00a76debf..7a6481983615b 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 @@ -37,7 +37,9 @@ 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.ql.type.DataTypes.VERSION; import static org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumnsTests.UNSIGNED_LONG_TEST_VERSIONS; +import static org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumnsTests.VERSION_FIELD_TEST_VERSIONS; import static org.mockito.Mockito.mock; public class SysTypesTests extends ESTestCase { @@ -95,6 +97,7 @@ public void testSysTypes() { "IP", "KEYWORD", "TEXT", + "VERSION", "BOOLEAN", "DATE", "TIME", @@ -160,6 +163,26 @@ public void testUnsignedLongFiltering() { } } + public void testVersionTypeFiltering() { + Set versions = new HashSet<>(VERSION_FIELD_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(VERSION, Version.fromId(cmd.v2().configuration().version().id)), + types.contains(VERSION.toString()) + ); + }, ex -> fail(ex.getMessage()))); + } + } + } + public void testSysTypesDefaultFiltering() { Tuple cmd = sql("SYS TYPES 0"); @@ -195,12 +218,14 @@ public void testSysTypesMultipleMatches() { cmd.v1().execute(cmd.v2(), wrap(p -> { SchemaRowSet r = (SchemaRowSet) p.rowSet(); - assertEquals(3, r.size()); + assertEquals(4, r.size()); assertEquals("IP", r.column(0)); assertTrue(r.advanceRow()); assertEquals("KEYWORD", r.column(0)); assertTrue(r.advanceRow()); assertEquals("TEXT", r.column(0)); + assertTrue(r.advanceRow()); + assertEquals("VERSION", r.column(0)); }, ex -> fail(ex.getMessage()))); } } 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 490fb935308d2..405ddbf4779fb 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 @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.ql.type.Converter; import org.elasticsearch.xpack.ql.type.DataType; import org.elasticsearch.xpack.sql.util.DateUtils; +import org.elasticsearch.xpack.versionfield.Version; import java.math.BigInteger; import java.time.OffsetTime; @@ -36,6 +37,7 @@ 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.VERSION; import static org.elasticsearch.xpack.sql.type.SqlDataTypeConverter.commonType; import static org.elasticsearch.xpack.sql.type.SqlDataTypeConverter.converterFor; import static org.elasticsearch.xpack.sql.type.SqlDataTypes.DATE; @@ -774,6 +776,27 @@ public void testIpToString() { assertEquals("10.0.0.1", ipToString.convert(stringToIp.convert(new Literal(s, "10.0.0.1", KEYWORD)))); } + public void testStringToVersion() { + Converter conversion = converterFor(randomFrom(KEYWORD, TEXT), VERSION); + assertNull(conversion.convert(null)); + assertEquals(new Version("2.1.4").toString(), conversion.convert("2.1.4").toString()); + assertEquals(new Version("2.1.4").toBytesRef(), ((Version) conversion.convert("2.1.4")).toBytesRef()); + assertEquals(new Version("2.1.4-SNAPSHOT").toString(), conversion.convert("2.1.4-SNAPSHOT").toString()); + assertEquals(new Version("2.1.4-SNAPSHOT").toBytesRef(), ((Version) conversion.convert("2.1.4-SNAPSHOT")).toBytesRef()); + } + + public void testVersionToString() { + Source s = new Source(Location.EMPTY, "2.1.4"); + Source s2 = new Source(Location.EMPTY, "2.1.4-SNAPSHOT"); + final DataType stringType = randomFrom(KEYWORD, TEXT); + Converter versionToString = converterFor(VERSION, stringType); + assertEquals("2.1.4", versionToString.convert(new Literal(s, "2.1.4", VERSION))); + assertEquals("2.1.4-SNAPSHOT", versionToString.convert(new Literal(s2, "2.1.4-SNAPSHOT", VERSION))); + Converter stringToVersion = converterFor(stringType, VERSION); + assertEquals("2.1.4", versionToString.convert(stringToVersion.convert(new Literal(s, "2.1.4", stringType)))); + assertEquals("2.1.4-SNAPSHOT", versionToString.convert(stringToVersion.convert(new Literal(s2, "2.1.4-SNAPSHOT", stringType)))); + } + private DataType randomInterval() { return randomFrom(SqlDataTypes.types().stream().filter(SqlDataTypes::isInterval).collect(toList())); } diff --git a/x-pack/plugin/sql/src/test/resources/org/elasticsearch/xpack/sql/planner/querytranslator_tests.txt b/x-pack/plugin/sql/src/test/resources/org/elasticsearch/xpack/sql/planner/querytranslator_tests.txt index d80d7d61908fd..63f4d2f6f9c98 100644 --- a/x-pack/plugin/sql/src/test/resources/org/elasticsearch/xpack/sql/planner/querytranslator_tests.txt +++ b/x-pack/plugin/sql/src/test/resources/org/elasticsearch/xpack/sql/planner/querytranslator_tests.txt @@ -102,6 +102,20 @@ SELECT date + 1 * INTERVAL '1' DAY FROM test GROUP BY 1; "missing_bucket":true,"value_type":"long","order":"asc"}}}]}}} ; +GroupByVersion +SELECT version, count(*) FROM test GROUP BY version; +{"terms":{"field":"version" +; + +GroupByVersionScript +SELECT CONCAT('1.', version::string)::version v, count(*) FROM test GROUP BY v; +{"terms":{"script":{"source":"InternalSqlScriptUtils.cast(InternalSqlScriptUtils.concat(params.v0,InternalSqlScriptUtils.cast(InternalQlScriptUtils.docValue(doc,params.v1),params.v2)),params.v3)","lang":"painless","params":{"v0":"1.","v1":"version","v2":"KEYWORD","v3":"VERSION"}} +; + +OrderByVersionScript +SELECT CONCAT('1.', version::string)::version v FROM test ORDER BY v; +{"script":{"source":"InternalQlScriptUtils.nullSafeSortString(InternalSqlScriptUtils.cast(InternalSqlScriptUtils.concat(params.v0,InternalSqlScriptUtils.cast(InternalQlScriptUtils.docValue(doc,params.v1),params.v2)),params.v3))","lang":"painless","params":{"v0":"1.","v1":"version","v2":"KEYWORD","v3":"VERSION"}} +; // Having /////////