Skip to content

Commit ae269f6

Browse files
committed
* Added a new ESQL query pragma:, NATIVE_FLOAT_TYPE, to avoid widening of floats to doubles (the current default behavior).
* Added a flag for ESQL's DataType's `widenSmallType`, to control to widening of floats * Threaded QueryPragmas into any place in theh code which eventually calls `widenSmallType`, to allow conditioning the float widening on the session / query specific pragmas. * For now, provided `QueryPragmas.EMPTY` as a hard coded argument for all the tests that now needs pragmas because of those changes. Ideally, next thing should be to inject pragmas into the tests from a single place, and using a Gradle command parameter. This will make sure that all the tests are running with the same pragmas values, and ` avoid redyndant degrees of freedom where each test manually pass its own value.
1 parent fe75f8d commit ae269f6

File tree

128 files changed

+5093
-552
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+5093
-552
lines changed

benchmarks/src/main/java/org/elasticsearch/benchmark/_nightly/esql/QueryPlanningBenchmark.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public void setup() {
116116
}
117117

118118
private LogicalPlan plan(EsqlParser parser, Analyzer analyzer, LogicalPlanOptimizer optimizer, String query) {
119-
var parsed = parser.createStatement(query, new QueryParams(), telemetry);
119+
var parsed = parser.createStatement(query, new QueryParams(), telemetry, );
120120
var analyzed = analyzer.analyze(parsed);
121121
var optimized = optimizer.optimize(analyzed);
122122
return optimized;

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DataType.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -653,8 +653,8 @@ public boolean isCounter() {
653653
* If this is a "small" numeric type this contains the type ESQL will
654654
* widen it into, otherwise this returns {@code this}.
655655
*/
656-
public DataType widenSmallNumeric() {
657-
return widenSmallNumeric == null ? this : widenSmallNumeric;
656+
public DataType widenSmallNumeric(boolean supportNativeFloat) {
657+
return widenSmallNumeric == null || supportNativeFloat ? this : widenSmallNumeric;
658658
}
659659

660660
/**

x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ public static Limit asLimit(Object node, Integer limitLiteral, Boolean duplicate
474474
}
475475

476476
public static Map<String, EsField> loadMapping(String name) {
477-
return LoadMapping.loadMapping(name);
477+
return new LoadMapping(QueryPragmas.EMPTY).loadMapping(name);
478478
}
479479

480480
public static String loadUtf8TextFile(String name) {

x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/LoadMapping.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.elasticsearch.xpack.esql.core.type.KeywordEsField;
1919
import org.elasticsearch.xpack.esql.core.type.TextEsField;
2020
import org.elasticsearch.xpack.esql.core.type.UnsupportedEsField;
21+
import org.elasticsearch.xpack.esql.plugin.QueryPragmas;
2122
import org.elasticsearch.xpack.esql.type.EsqlDataTypeRegistry;
2223

2324
import java.io.IOException;
@@ -36,13 +37,19 @@
3637
import static org.junit.Assert.assertNotNull;
3738

3839
public class LoadMapping {
39-
public static Map<String, EsField> loadMapping(String name) {
40+
public LoadMapping(QueryPragmas pragmas) {
41+
this.pragmas = pragmas;
42+
}
43+
44+
private final QueryPragmas pragmas;
45+
46+
public Map<String, EsField> loadMapping(String name) {
4047
InputStream stream = LoadMapping.class.getResourceAsStream("/" + name);
4148
assertNotNull("Could not find mapping resource:" + name, stream);
4249
return loadMapping(stream);
4350
}
4451

45-
private static Map<String, EsField> loadMapping(InputStream stream) {
52+
private Map<String, EsField> loadMapping(InputStream stream) {
4653
try (InputStream in = stream) {
4754
Map<String, Object> map = XContentHelper.convertToMap(JsonXContent.jsonXContent, in, true);
4855
return fromEs(map);
@@ -52,15 +59,15 @@ private static Map<String, EsField> loadMapping(InputStream stream) {
5259
}
5360

5461
@SuppressWarnings("unchecked")
55-
private static Map<String, EsField> fromEs(Map<String, Object> asMap) {
62+
private Map<String, EsField> fromEs(Map<String, Object> asMap) {
5663
Map<String, Object> props = null;
5764
if (asMap != null && asMap.isEmpty() == false) {
5865
props = (Map<String, Object>) asMap.get("properties");
5966
}
6067
return props == null || props.isEmpty() ? emptyMap() : startWalking(props);
6168
}
6269

63-
private static Map<String, EsField> startWalking(Map<String, Object> mapping) {
70+
private Map<String, EsField> startWalking(Map<String, Object> mapping) {
6471
Map<String, EsField> types = new LinkedHashMap<>();
6572

6673
if (mapping == null) {
@@ -74,7 +81,7 @@ private static Map<String, EsField> startWalking(Map<String, Object> mapping) {
7481
}
7582

7683
@SuppressWarnings("unchecked")
77-
private static void walkMapping(String name, Object value, Map<String, EsField> mapping) {
84+
private void walkMapping(String name, Object value, Map<String, EsField> mapping) {
7885
// object type - only root or nested docs supported
7986
if (value instanceof Map) {
8087
Map<String, Object> content = (Map<String, Object>) value;
@@ -122,7 +129,7 @@ private static void walkMapping(String name, Object value, Map<String, EsField>
122129
}
123130
}
124131

125-
private static DataType getType(Map<String, Object> content) {
132+
private DataType getType(Map<String, Object> content) {
126133
if (content.containsKey("type")) {
127134
String typeName = content.get("type").toString();
128135
if ("constant_keyword".equals(typeName) || "wildcard".equals(typeName)) {
@@ -136,7 +143,7 @@ private static DataType getType(Map<String, Object> content) {
136143
metricType = (TimeSeriesParams.MetricType) metricsTypeParameter;
137144
}
138145
try {
139-
return EsqlDataTypeRegistry.INSTANCE.fromEs(typeName, metricType);
146+
return EsqlDataTypeRegistry.INSTANCE.fromEs(typeName, metricType, pragmas);
140147
} catch (IllegalArgumentException ex) {
141148
return UNSUPPORTED;
142149
}

x-pack/plugin/esql/qa/testFixtures/src/main/resources/floats.csv-spec

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,3 +692,11 @@ null
692692
null
693693
null
694694
;
695+
696+
697+
foo
698+
row d = [3.141] | eval dd = to_double(d);
699+
700+
d:double |dd:double
701+
[3.141] |3.142
702+
;

x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/action/LookupJoinTypesIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/ql
22
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
33
* or more contributor license agreements. Licensed under the Elastic License
44
* 2.0; you may not use this file except in compliance with the Elastic License
@@ -514,7 +514,7 @@ private void addFails(DataType mainType, DataType lookupType) {
514514
Locale.ROOT,
515515
"JOIN left field [%s] of type [%s] is incompatible with right field [%s] of type [%s]",
516516
fieldName,
517-
mainType.widenSmallNumeric(),
517+
mainType.widenSmallNumeric(configuration.),
518518
fieldName,
519519
lookupType.widenSmallNumeric()
520520
);

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject;
107107
import org.elasticsearch.xpack.esql.plan.logical.local.LocalRelation;
108108
import org.elasticsearch.xpack.esql.plan.logical.local.LocalSupplier;
109+
import org.elasticsearch.xpack.esql.plugin.QueryPragmas;
109110
import org.elasticsearch.xpack.esql.rule.ParameterizedRule;
110111
import org.elasticsearch.xpack.esql.rule.ParameterizedRuleExecutor;
111112
import org.elasticsearch.xpack.esql.rule.Rule;
@@ -212,18 +213,18 @@ protected List<Batch<LogicalPlan>> batches() {
212213
}
213214

214215
private static class ResolveTable extends ParameterizedAnalyzerRule<UnresolvedRelation, AnalyzerContext> {
215-
216216
@Override
217217
protected LogicalPlan rule(UnresolvedRelation plan, AnalyzerContext context) {
218218
return resolveIndex(
219219
plan,
220220
plan.indexMode().equals(IndexMode.LOOKUP)
221221
? context.lookupResolution().get(plan.indexPattern().indexPattern())
222-
: context.indexResolution()
222+
: context.indexResolution(),
223+
context.configuration().pragmas()
223224
);
224225
}
225226

226-
private LogicalPlan resolveIndex(UnresolvedRelation plan, IndexResolution indexResolution) {
227+
private LogicalPlan resolveIndex(UnresolvedRelation plan, IndexResolution indexResolution, QueryPragmas pragmas) {
227228
if (indexResolution == null || indexResolution.isValid() == false) {
228229
String indexResolutionMessage = indexResolution == null ? "[none specified]" : indexResolution.toString();
229230
return plan.unresolvedMessage().equals(indexResolutionMessage)
@@ -254,7 +255,7 @@ private LogicalPlan resolveIndex(UnresolvedRelation plan, IndexResolution indexR
254255

255256
EsIndex esIndex = indexResolution.get();
256257

257-
var attributes = mappingAsAttributes(plan.source(), esIndex.mapping());
258+
var attributes = mappingAsAttributes(plan.source(), esIndex.mapping(), pragmas);
258259
attributes.addAll(plan.metadataFields());
259260
return new EsRelation(
260261
plan.source(),
@@ -264,6 +265,8 @@ private LogicalPlan resolveIndex(UnresolvedRelation plan, IndexResolution indexR
264265
attributes.isEmpty() ? NO_FIELDS : attributes
265266
);
266267
}
268+
269+
private QueryPragmas pragmas;
267270
}
268271

269272
/**
@@ -274,22 +277,28 @@ private LogicalPlan resolveIndex(UnresolvedRelation plan, IndexResolution indexR
274277
* Public for testing.
275278
* </p>
276279
*/
277-
public static List<Attribute> mappingAsAttributes(Source source, Map<String, EsField> mapping) {
280+
public static List<Attribute> mappingAsAttributes(Source source, Map<String, EsField> mapping, QueryPragmas pragmas) {
278281
var list = new ArrayList<Attribute>();
279-
mappingAsAttributes(list, source, null, mapping);
282+
mappingAsAttributes(list, source, null, mapping, pragmas);
280283
list.sort(Comparator.comparing(Attribute::name));
281284
return list;
282285
}
283286

284-
private static void mappingAsAttributes(List<Attribute> list, Source source, String parentName, Map<String, EsField> mapping) {
287+
private static void mappingAsAttributes(
288+
List<Attribute> list,
289+
Source source,
290+
String parentName,
291+
Map<String, EsField> mapping,
292+
QueryPragmas pragmas
293+
) {
285294
for (Map.Entry<String, EsField> entry : mapping.entrySet()) {
286295
String name = entry.getKey();
287296
EsField t = entry.getValue();
288297

289298
if (t != null) {
290299
name = parentName == null ? name : parentName + "." + name;
291300
var fieldProperties = t.getProperties();
292-
var type = t.getDataType().widenSmallNumeric();
301+
var type = t.getDataType().widenSmallNumeric(pragmas.native_float_type());
293302
// due to a bug also copy the field since the Attribute hierarchy extracts the data type
294303
// directly even if the data type is passed explicitly
295304
if (type != t.getDataType()) {
@@ -305,7 +314,7 @@ private static void mappingAsAttributes(List<Attribute> list, Source source, Str
305314
}
306315
// allow compound object even if they are unknown
307316
if (fieldProperties.isEmpty() == false) {
308-
mappingAsAttributes(list, source, attribute.name(), fieldProperties);
317+
mappingAsAttributes(list, source, attribute.name(), fieldProperties, pragmas);
309318
}
310319
}
311320
}
@@ -329,7 +338,7 @@ protected LogicalPlan rule(Enrich plan, AnalyzerContext context) {
329338
List<NamedExpression> enrichFields = calculateEnrichFields(
330339
plan.source(),
331340
policyName,
332-
mappingAsAttributes(plan.source(), resolved.mapping()),
341+
mappingAsAttributes(plan.source(), resolved.mapping(), context.configuration().pragmas()),
333342
plan.enrichFields(),
334343
policy
335344
);
@@ -1391,16 +1400,16 @@ public LogicalPlan apply(LogicalPlan plan, AnalyzerContext context) {
13911400
// do implicit casting for function arguments
13921401
return plan.transformExpressionsUp(
13931402
org.elasticsearch.xpack.esql.core.expression.function.Function.class,
1394-
e -> ImplicitCasting.cast(e, context.functionRegistry().snapshotRegistry())
1403+
e -> ImplicitCasting.cast(e, context.functionRegistry().snapshotRegistry(), context.configuration().pragmas())
13951404
);
13961405
}
13971406

1398-
private static Expression cast(org.elasticsearch.xpack.esql.core.expression.function.Function f, EsqlFunctionRegistry registry) {
1407+
private static Expression cast(org.elasticsearch.xpack.esql.core.expression.function.Function f, EsqlFunctionRegistry registry, QueryPragmas pragmas) {
13991408
if (f instanceof In in) {
14001409
return processIn(in);
14011410
}
14021411
if (f instanceof EsqlScalarFunction || f instanceof GroupingFunction) { // exclude AggregateFunction until it is needed
1403-
return processScalarOrGroupingFunction(f, registry);
1412+
return processScalarOrGroupingFunction(f, registry, pragmas);
14041413
}
14051414
if (f instanceof EsqlArithmeticOperation || f instanceof BinaryComparison) {
14061415
return processBinaryOperator((BinaryOperator) f);
@@ -1413,7 +1422,8 @@ private static Expression cast(org.elasticsearch.xpack.esql.core.expression.func
14131422

14141423
private static Expression processScalarOrGroupingFunction(
14151424
org.elasticsearch.xpack.esql.core.expression.function.Function f,
1416-
EsqlFunctionRegistry registry
1425+
EsqlFunctionRegistry registry,
1426+
QueryPragmas pragmas
14171427
) {
14181428
List<Expression> args = f.arguments();
14191429
List<DataType> targetDataTypes = registry.getDataTypeForStringLiteralConversion(f.getClass());
@@ -1456,7 +1466,7 @@ private static Expression processScalarOrGroupingFunction(
14561466
}
14571467
Expression resultF = childrenChanged ? f.replaceChildren(newChildren) : f;
14581468
return targetNumericType != null && castNumericArgs
1459-
? castMixedNumericTypes((EsqlScalarFunction) resultF, targetNumericType)
1469+
? castMixedNumericTypes((EsqlScalarFunction) resultF, targetNumericType, pragmas)
14601470
: resultF;
14611471
}
14621472

@@ -1532,7 +1542,7 @@ private static boolean canCastNumeric(DataType from, DataType to) {
15321542
return commonType == to;
15331543
}
15341544

1535-
private static Expression castMixedNumericTypes(EsqlScalarFunction f, DataType targetNumericType) {
1545+
private static Expression castMixedNumericTypes(EsqlScalarFunction f, DataType targetNumericType, QueryPragmas pragmas) {
15361546
List<Expression> newChildren = new ArrayList<>(f.children().size());
15371547
boolean childrenChanged = false;
15381548
DataType childDataType;
@@ -1549,10 +1559,10 @@ private static Expression castMixedNumericTypes(EsqlScalarFunction f, DataType t
15491559
childrenChanged = true;
15501560
// add a casting function
15511561
switch (targetNumericType) {
1552-
case INTEGER -> newChildren.add(new ToInteger(e.source(), e));
1553-
case LONG -> newChildren.add(new ToLong(e.source(), e));
1554-
case DOUBLE -> newChildren.add(new ToDouble(e.source(), e));
1555-
case UNSIGNED_LONG -> newChildren.add(new ToUnsignedLong(e.source(), e));
1562+
case INTEGER -> newChildren.add(new ToInteger(e.source(), e, pragmas));
1563+
case LONG -> newChildren.add(new ToLong(e.source(), e, pragmas));
1564+
case DOUBLE -> newChildren.add(new ToDouble(e.source(), e, pragmas));
1565+
case UNSIGNED_LONG -> newChildren.add(new ToUnsignedLong(e.source(), e, pragmas));
15561566
default -> throw new EsqlIllegalArgumentException("unexpected data type: " + targetNumericType);
15571567
}
15581568
} else {
@@ -1720,7 +1730,7 @@ private Expression resolveConvertFunction(ConvertFunction convert, List<FieldAtt
17201730
return fcf.replaceChildren(Collections.singletonList(ua));
17211731
}
17221732
imf.types().forEach(type -> {
1723-
if (supportedTypes.contains(type.widenSmallNumeric())) {
1733+
if (supportedTypes.contains(type.widenSmallNumeric(convert.getPragmas().native_float_type()))) {
17241734
typeResolutions(fa, convert, type, imf, typeResolutions);
17251735
}
17261736
});
@@ -1745,7 +1755,7 @@ private Expression resolveConvertFunction(ConvertFunction convert, List<FieldAtt
17451755
// Data type is different between implicit(date_nanos) and explicit casting, if the conversion is supported, create a
17461756
// new MultiTypeEsField with explicit casting type, and add it to unionFieldAttributes.
17471757
Set<DataType> supportedTypes = convert.supportedTypes();
1748-
if (supportedTypes.contains(fa.dataType()) && canConvertOriginalTypes(mtf, supportedTypes)) {
1758+
if (supportedTypes.contains(fa.dataType()) && canConvertOriginalTypes(mtf, supportedTypes, convert.getPragmas())) {
17491759
// Build the mapping between index name and conversion expressions
17501760
Map<String, Expression> indexToConversionExpressions = new HashMap<>();
17511761
for (Map.Entry<String, Expression> entry : mtf.getIndexToConversionExpressions().entrySet()) {
@@ -1807,13 +1817,13 @@ private static MultiTypeEsField resolvedMultiTypeEsField(
18071817
return MultiTypeEsField.resolveFrom(imf, typesToConversionExpressions);
18081818
}
18091819

1810-
private static boolean canConvertOriginalTypes(MultiTypeEsField multiTypeEsField, Set<DataType> supportedTypes) {
1820+
private static boolean canConvertOriginalTypes(MultiTypeEsField multiTypeEsField, Set<DataType> supportedTypes, QueryPragmas pragmas) {
18111821
return multiTypeEsField.getIndexToConversionExpressions()
18121822
.values()
18131823
.stream()
18141824
.allMatch(
18151825
e -> e instanceof AbstractConvertFunction convertFunction
1816-
&& supportedTypes.contains(convertFunction.field().dataType().widenSmallNumeric())
1826+
&& supportedTypes.contains(convertFunction.field().dataType().widenSmallNumeric(pragmas.native_float_type()))
18171827
);
18181828
}
18191829

@@ -1911,7 +1921,7 @@ public LogicalPlan apply(LogicalPlan plan) {
19111921
return relation.transformExpressionsUp(FieldAttribute.class, f -> {
19121922
if (f.field() instanceof InvalidMappedField imf && imf.types().stream().allMatch(DataType::isDate)) {
19131923
HashMap<ResolveUnionTypes.TypeResolutionKey, Expression> typeResolutions = new HashMap<>();
1914-
var convert = new ToDateNanos(f.source(), f);
1924+
var convert = new ToDateNanos(f.source(), f, QueryPragmas.EMPTY); // TODO thread actual pragmas!
19151925
imf.types().forEach(type -> typeResolutions(f, convert, type, imf, typeResolutions));
19161926
var resolvedField = ResolveUnionTypes.resolvedMultiTypeEsField(f, typeResolutions);
19171927
return new FieldAttribute(f.source(), f.parentName(), f.name(), resolvedField, f.nullable(), f.id(), f.synthetic());

0 commit comments

Comments
 (0)