Skip to content
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
870db5f
ESQL: Test fetching old types
nik9000 Sep 22, 2025
c541fbd
[CI] Auto commit changes from spotless
Sep 22, 2025
ef1da90
Drop unused
nik9000 Sep 22, 2025
6d546f9
Merge remote-tracking branch 'nik9000/esql_fetch_field_types' into es…
nik9000 Sep 22, 2025
c5c40cb
[CI] Update transport version definitions
Sep 22, 2025
ce91be1
better logging
nik9000 Sep 23, 2025
dae8a4a
Merge remote-tracking branch 'nik9000/estql_fetch_field_types' into e…
nik9000 Sep 23, 2025
d7f00df
Merge branch 'main' into esql_fetch_field_types
nik9000 Sep 23, 2025
654abde
[CI] Auto commit changes from spotless
Sep 23, 2025
1945205
Fix?
nik9000 Sep 23, 2025
aae54e9
Merge remote-tracking branch 'nik9000/esql_fetch_field_types' into es…
nik9000 Sep 23, 2025
67b4e73
Multi
nik9000 Sep 23, 2025
3780362
[CI] Auto commit changes from spotless
Sep 23, 2025
5249481
[CI] Update transport version definitions
Sep 23, 2025
09c4eb9
Only enable if fn or ts
nik9000 Sep 23, 2025
72d338f
Merge remote-tracking branch 'nik9000/esql_fetch_field_types' into es…
nik9000 Sep 23, 2025
d25151c
[CI] Auto commit changes from spotless
Sep 23, 2025
280da8a
impl
nik9000 Sep 23, 2025
abd851a
Merge remote-tracking branch 'nik9000/esql_fetch_field_types' into es…
nik9000 Sep 23, 2025
206560c
Merge branch 'main' into esql_fetch_field_types
nik9000 Sep 23, 2025
b9fee12
Hack
nik9000 Sep 23, 2025
a1d2389
Change error
nik9000 Sep 24, 2025
61110ab
Merge branch 'main' into esql_fetch_field_types
nik9000 Sep 24, 2025
ef0c269
Fetch dense_Vector
nik9000 Sep 24, 2025
663c15a
Integ tests
nik9000 Sep 24, 2025
1da11c7
to_dense_Vector
nik9000 Sep 24, 2025
a76afc1
Drop score
nik9000 Sep 24, 2025
1e8c58b
More score
nik9000 Sep 24, 2025
90e21a0
Move created version
nik9000 Sep 24, 2025
c30668f
Test better
nik9000 Sep 24, 2025
77b59d4
More explain
nik9000 Sep 24, 2025
a515924
Comments
nik9000 Sep 24, 2025
9102fcd
Merge branch 'main' into esql_fetch_field_types
nik9000 Sep 24, 2025
8f2d210
ibwc
nik9000 Sep 25, 2025
8b6c45f
Extra
nik9000 Sep 25, 2025
c60a704
Udpate
nik9000 Sep 25, 2025
f38c8a1
Merge branch 'main' into esql_fetch_field_types
nik9000 Sep 25, 2025
78a85db
[CI] Auto commit changes from spotless
Sep 25, 2025
86005ad
Merge branch 'main' into esql_fetch_field_types
nik9000 Sep 25, 2025
9919907
mor efix
nik9000 Sep 25, 2025
fef79ba
Merge branch 'main' into esql_fetch_field_types
nik9000 Sep 25, 2025
60e7e05
Merge remote-tracking branch 'nik9000/esql_fetch_field_types' into es…
nik9000 Sep 25, 2025
435d233
Format
nik9000 Sep 25, 2025
940a3b2
Add other
nik9000 Sep 25, 2025
d4cfad8
Log
nik9000 Sep 25, 2025
94784ab
Some udpates
nik9000 Sep 25, 2025
0d9c027
Merge branch 'main' into esql_fetch_field_types
nik9000 Sep 25, 2025
d2a5064
[CI] Auto commit changes from spotless
Sep 25, 2025
d172d26
Test case
nik9000 Sep 25, 2025
20472de
Merge remote-tracking branch 'nik9000/esql_fetch_field_types' into es…
nik9000 Sep 25, 2025
280daf8
updates one last time please please
nik9000 Sep 26, 2025
85771e7
[CI] Auto commit changes from spotless
Sep 26, 2025
cd0ce72
Merge branch 'main' into esql_fetch_field_types
nik9000 Sep 26, 2025
e71115c
Merge remote-tracking branch 'nik9000/esql_fetch_field_types' into es…
nik9000 Sep 26, 2025
d694176
spotless
nik9000 Sep 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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.esql.core.type;

import org.elasticsearch.TransportVersion;

/**
* Version that supports a {@link DataType}.
*/
public interface CreatedVersion {
boolean supports(TransportVersion version);

CreatedVersion SUPPORTED_ON_ALL_NODES = new CreatedVersion() {
@Override
public boolean supports(TransportVersion version) {
return true;
}

@Override
public String toString() {
return "SupportedOnAllVersions";
}
};

static CreatedVersion supportedOn(TransportVersion createdVersion) {
return new CreatedVersion() {
@Override
public boolean supports(TransportVersion version) {
return version.supports(createdVersion);
}

@Override
public String toString() {
return "SupportedOn[" + createdVersion + "]";
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
package org.elasticsearch.xpack.esql.core.type;

import org.apache.lucene.util.BytesRef;
import org.elasticsearch.TransportVersion;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.util.FeatureFlag;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.mapper.SourceFieldMapper;
Expand All @@ -32,6 +34,8 @@
import java.util.function.Function;

import static java.util.stream.Collectors.toMap;
import static org.elasticsearch.TransportVersions.INFERENCE_REQUEST_ADAPTIVE_RATE_LIMITING;
import static org.elasticsearch.TransportVersions.ML_INFERENCE_SAGEMAKER_CHAT_COMPLETION;

/**
* This enum represents data types the ES|QL query processing layer is able to
Expand Down Expand Up @@ -140,7 +144,7 @@
* unsupported types.</li>
* </ul>
*/
public enum DataType {
public enum DataType implements Writeable {
/**
* Fields of this type are unsupported by any functions and are always
* rendered as {@code null} in the response.
Expand Down Expand Up @@ -306,12 +310,26 @@ public enum DataType {
*/
PARTIAL_AGG(builder().esType("partial_agg").estimatedSize(1024)),

AGGREGATE_METRIC_DOUBLE(builder().esType("aggregate_metric_double").estimatedSize(Double.BYTES * 3 + Integer.BYTES)),
AGGREGATE_METRIC_DOUBLE(
builder().esType("aggregate_metric_double")
.estimatedSize(Double.BYTES * 3 + Integer.BYTES)
.createdVersion(
// Version created just *after* we committed support for aggregate_metric_double
INFERENCE_REQUEST_ADAPTIVE_RATE_LIMITING
)
),

/**
* Fields with this type are dense vectors, represented as an array of double values.
*/
DENSE_VECTOR(builder().esType("dense_vector").estimatedSize(4096));
DENSE_VECTOR(
builder().esType("dense_vector")
.estimatedSize(4096)
.createdVersion(
// Version created just *after* we committed support for dense_vector
ML_INFERENCE_SAGEMAKER_CHAT_COMPLETION
)
);

/**
* Types that are actively being built. These types are
Expand Down Expand Up @@ -375,6 +393,11 @@ public enum DataType {
*/
private final DataType counter;

/**
* Version that first created this data type.
*/
private final CreatedVersion createdVersion;

DataType(Builder builder) {
String typeString = builder.typeName != null ? builder.typeName : builder.esType;
this.typeName = typeString.toLowerCase(Locale.ROOT);
Expand All @@ -387,6 +410,7 @@ public enum DataType {
this.isCounter = builder.isCounter;
this.widenSmallNumeric = builder.widenSmallNumeric;
this.counter = builder.counter;
this.createdVersion = builder.createdVersion;
}

private static final Collection<DataType> TYPES = Arrays.stream(values())
Expand Down Expand Up @@ -727,7 +751,20 @@ public DataType counter() {
return counter;
}

@Override
public void writeTo(StreamOutput out) throws IOException {
if (createdVersion.supports(out.getTransportVersion()) == false) {
/*
* TODO when we implement version aware planning flip this to an IllegalStateException
* so we throw a 500 error. It'll be our bug then. Right now it's a sign that the user
* tried to do something like `KNN(dense_vector_field, [1, 2])` against an old node.
* Like, during the rolling upgrade that enables KNN or to a remote cluster that has
* not yet been upgraded.
*/
throw new IllegalArgumentException(
"remote node at version [" + out.getTransportVersion() + "] doesn't understand data type [" + this + "]"
);
}
((PlanStreamOutput) out).writeCachedString(typeName);
}

Expand Down Expand Up @@ -779,6 +816,10 @@ public boolean isDate() {
};
}

public CreatedVersion createdVersion() {
return createdVersion;
}

public static DataType suggestedCast(Set<DataType> originalTypes) {
if (originalTypes.isEmpty() || originalTypes.contains(UNSUPPORTED)) {
return null;
Expand Down Expand Up @@ -846,6 +887,13 @@ private static class Builder {
*/
private DataType counter;

/**
* The version when this data type was created. We default to the first
* version for which we maintain wire compatibility, which is pretty
* much {@code 8.18.0}.
*/
private CreatedVersion createdVersion = CreatedVersion.SUPPORTED_ON_ALL_NODES;

Builder() {}

Builder esType(String esType) {
Expand Down Expand Up @@ -901,5 +949,10 @@ Builder counter(DataType counter) {
this.counter = counter;
return this;
}

Builder createdVersion(TransportVersion createdVersion) {
this.createdVersion = CreatedVersion.supportedOn(createdVersion);
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.esql.qa.mixed;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;

import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.test.TestClustersThreadFilter;
import org.elasticsearch.test.cluster.ElasticsearchCluster;
import org.elasticsearch.xpack.esql.qa.rest.AllSupportedFieldsTestCase;
import org.junit.ClassRule;

/**
* Fetch all field types in a mixed version cluster, simulating a rolling upgrade.
*/
@ThreadLeakFilters(filters = TestClustersThreadFilter.class)
public class AllSupportedFieldsIT extends AllSupportedFieldsTestCase {
@ClassRule
public static ElasticsearchCluster cluster = Clusters.mixedVersionCluster();

public AllSupportedFieldsIT(MappedFieldType.FieldExtractPreference extractPreference, IndexMode indexMode) {
super(extractPreference, indexMode);
}

@Override
protected String getTestRestCluster() {
return cluster.getHttpAddresses();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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.esql.ccq;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.core.IOUtils;
import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.test.TestClustersThreadFilter;
import org.elasticsearch.test.cluster.ElasticsearchCluster;
import org.elasticsearch.xpack.esql.qa.rest.AllSupportedFieldsTestCase;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;

import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;

/**
* Fetch all field types via cross cluster search, possible on a different version.
*/
@ThreadLeakFilters(filters = TestClustersThreadFilter.class)
public class AllSupportedFieldsIT extends AllSupportedFieldsTestCase {
static ElasticsearchCluster remoteCluster = Clusters.remoteCluster();
static ElasticsearchCluster localCluster = Clusters.localCluster(remoteCluster);

@ClassRule
public static TestRule clusterRule = RuleChain.outerRule(remoteCluster).around(localCluster);

private static RestClient remoteClient;
private static Map<String, NodeInfo> remoteNodeToInfo;

public AllSupportedFieldsIT(MappedFieldType.FieldExtractPreference extractPreference, IndexMode indexMode) {
super(extractPreference, indexMode);
}

@Before
public void createRemoteIndices() throws IOException {
for (Map.Entry<String, NodeInfo> e : remoteNodeToInfo().entrySet()) {
createIndexForNode(remoteClient(), e.getKey(), e.getValue().id());
}
}

private Map<String, NodeInfo> remoteNodeToInfo() throws IOException {
if (remoteNodeToInfo == null) {
remoteNodeToInfo = fetchNodeToInfo(remoteClient(), "remote_cluster");
}
return remoteNodeToInfo;
}

@Override
protected Map<String, NodeInfo> allNodeToInfo() throws IOException {
Map<String, NodeInfo> all = new TreeMap<>();
all.putAll(super.allNodeToInfo());
all.putAll(remoteNodeToInfo());
return all;
}

private RestClient remoteClient() throws IOException {
if (remoteClient == null) {
var clusterHosts = parseClusterHosts(remoteCluster.getHttpAddresses());
remoteClient = buildClient(restClientSettings(), clusterHosts.toArray(new HttpHost[0]));
}
return remoteClient;
}

@Override
protected String getTestRestCluster() {
return localCluster.getHttpAddresses();
}

@AfterClass
public static void closeRemoteClient() throws IOException {
try {
IOUtils.close(remoteClient);
} finally {
remoteClient = null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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.esql.qa.single_node;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;

import org.elasticsearch.index.IndexMode;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.test.TestClustersThreadFilter;
import org.elasticsearch.test.cluster.ElasticsearchCluster;
import org.elasticsearch.xpack.esql.qa.rest.AllSupportedFieldsTestCase;
import org.junit.ClassRule;

/**
* Simple test for fetching all supported field types.
*/
@ThreadLeakFilters(filters = TestClustersThreadFilter.class)
public class AllSupportedFieldsIT extends AllSupportedFieldsTestCase {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question for single-node. This is not going to serialize at all

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's useful here because it serves as a smoke test for the ground "it works" state. Kind of a self test for the test.

@ClassRule
public static ElasticsearchCluster cluster = Clusters.testCluster(c -> {});

public AllSupportedFieldsIT(MappedFieldType.FieldExtractPreference extractPreference, IndexMode indexMode) {
super(extractPreference, indexMode);
}

@Override
protected String getTestRestCluster() {
return cluster.getHttpAddresses();
}
}
Loading