106106import org .elasticsearch .xpack .esql .plan .logical .local .EsqlProject ;
107107import org .elasticsearch .xpack .esql .plan .logical .local .LocalRelation ;
108108import org .elasticsearch .xpack .esql .plan .logical .local .LocalSupplier ;
109+ import org .elasticsearch .xpack .esql .plugin .QueryPragmas ;
109110import org .elasticsearch .xpack .esql .rule .ParameterizedRule ;
110111import org .elasticsearch .xpack .esql .rule .ParameterizedRuleExecutor ;
111112import 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