diff --git a/muted-tests.yml b/muted-tests.yml index 43751212930b2..9a6cfdf3d9b50 100644 --- a/muted-tests.yml +++ b/muted-tests.yml @@ -408,9 +408,6 @@ tests: - class: org.elasticsearch.xpack.esql.heap_attack.HeapAttackIT method: testLookupExplosionNoFetch issue: https://github.com/elastic/elasticsearch/issues/127365 -- class: org.elasticsearch.xpack.esql.qa.single_node.GenerativeIT - method: test - issue: https://github.com/elastic/elasticsearch/issues/127536 - class: org.elasticsearch.xpack.test.rest.XPackRestIT method: test {p0=ml/data_frame_analytics_cat_apis/Test cat data frame analytics all jobs with header} issue: https://github.com/elastic/elasticsearch/issues/127625 diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/EsqlQueryGenerator.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/EsqlQueryGenerator.java index 7fbdc292afdbf..31fddae7c6859 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/EsqlQueryGenerator.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/EsqlQueryGenerator.java @@ -163,7 +163,7 @@ private static String grok(List previousOutput) { result.append(randomIdentifier()); } else { String fieldName = randomRawName(previousOutput); - if (fieldName.isEmpty()) { // it's a bug, managed later, skipping for now + if (fieldName == null) { fieldName = randomIdentifier(); } result.append(fieldName); @@ -191,7 +191,7 @@ private static String dissect(List previousOutput) { result.append(randomIdentifier()); } else { String fieldName = randomRawName(previousOutput); - if (fieldName.isEmpty()) { // it's a bug, managed later, skipping for now + if (fieldName == null) { fieldName = randomIdentifier(); } result.append(fieldName); @@ -210,6 +210,9 @@ private static String keep(List previousOutput) { proj.add("*"); } else { String name = randomName(previousOutput); + if (name == null) { + continue; + } if (name.length() > 1 && name.startsWith("`") == false && randomIntBetween(0, 100) < 10) { if (randomBoolean()) { name = name.substring(0, randomIntBetween(1, name.length() - 1)) + "*"; @@ -220,31 +223,42 @@ private static String keep(List previousOutput) { proj.add(name); } } + if (proj.isEmpty()) { + return ""; + } return " | keep " + proj.stream().collect(Collectors.joining(", ")); } private static String randomName(List previousOutput) { String result = randomRawName(previousOutput); - if ((randomBoolean() && result.contains("*") == false)) { + if (result == null) { + return null; + } + if (randomBoolean() && result.contains("*") == false) { result = "`" + result + "`"; } return result; } + /** + * Returns a field name from a list of columns. + * Could be null if none of the fields can be considered + */ private static String randomRawName(List previousOutput) { - // we need to exclude - // https://github.com/elastic/elasticsearch/issues/121741 - String result = randomFrom(previousOutput.stream().filter(x -> x.name().equals("") == false).toList()).name(); + var list = previousOutput.stream().filter(EsqlQueryGenerator::fieldCanBeUsed).toList(); + if (list.isEmpty()) { + return null; + } + String result = randomFrom(list).name(); return result; } + /** + * Returns a field that can be used for grouping. + * Can return null + */ private static String randomGroupableName(List previousOutput) { - // we need to exclude - // https://github.com/elastic/elasticsearch/issues/121741 - var candidates = previousOutput.stream() - .filter(EsqlQueryGenerator::groupable) - .filter(x -> x.name().equals("") == false) - .toList(); + var candidates = previousOutput.stream().filter(EsqlQueryGenerator::groupable).filter(EsqlQueryGenerator::fieldCanBeUsed).toList(); if (candidates.isEmpty()) { return null; } @@ -260,13 +274,12 @@ private static boolean groupable(Column col) { || col.type.equals("version"); } + /** + * returns a field that can be sorted. + * Null if no fields are sortable. + */ private static String randomSortableName(List previousOutput) { - // we need to exclude - // https://github.com/elastic/elasticsearch/issues/121741 - var candidates = previousOutput.stream() - .filter(EsqlQueryGenerator::sortable) - .filter(x -> x.name().equals("") == false) - .toList(); + var candidates = previousOutput.stream().filter(EsqlQueryGenerator::sortable).filter(EsqlQueryGenerator::fieldCanBeUsed).toList(); if (candidates.isEmpty()) { return null; } @@ -290,10 +303,18 @@ private static String rename(List previousOutput) { for (Column column : previousOutput) { nameToType.put(column.name, column.type); } - List names = new ArrayList<>(previousOutput.stream().map(Column::name).collect(Collectors.toList())); + List names = new ArrayList<>( + previousOutput.stream().filter(EsqlQueryGenerator::fieldCanBeUsed).map(Column::name).collect(Collectors.toList()) + ); + if (names.isEmpty()) { + return ""; + } for (int i = 0; i < n; i++) { + if (names.isEmpty()) { + break; + } var name = randomFrom(names); - if (name.equals("") || nameToType.get(name).endsWith("_range")) { + if (nameToType.get(name).endsWith("_range")) { // ranges are not fully supported yet continue; } @@ -306,9 +327,6 @@ private static String rename(List previousOutput) { } else { newName = names.get(randomIntBetween(0, names.size() - 1)); } - if (newName.equals("")) { // it's a bug, managed as an error later - continue; - } nameToType.put(newName, nameToType.get(name)); if (randomBoolean() && name.startsWith("`") == false) { name = "`" + name + "`"; @@ -332,6 +350,9 @@ private static String drop(List previousOutput) { Set proj = new HashSet<>(); for (int i = 0; i < n; i++) { String name = randomRawName(previousOutput); + if (name == null) { + continue; + } if (name.length() > 1 && name.startsWith("`") == false && randomIntBetween(0, 100) < 10) { if (randomBoolean()) { name = name.substring(0, randomIntBetween(1, name.length() - 1)) + "*"; @@ -343,6 +364,9 @@ private static String drop(List previousOutput) { } proj.add(name); } + if (proj.isEmpty()) { + return ""; + } return " | drop " + proj.stream().collect(Collectors.joining(", ")); } @@ -363,7 +387,11 @@ private static String sort(List previousOutput) { } private static String mvExpand(List previousOutput) { - return " | mv_expand " + randomName(previousOutput); + String toExpand = randomName(previousOutput); + if (toExpand == null) { + return ""; // no columns to expand + } + return " | mv_expand " + toExpand; } private static String eval(List previousOutput) { @@ -376,6 +404,9 @@ private static String eval(List previousOutput) { name = randomIdentifier(); } else { name = randomName(previousOutput); + if (name == null) { + name = randomIdentifier(); + } } String expression = expression(previousOutput); if (i > 0) { @@ -390,7 +421,10 @@ private static String eval(List previousOutput) { } private static String stats(List previousOutput) { - List nonNull = previousOutput.stream().filter(x -> x.type().equals("null") == false).collect(Collectors.toList()); + List nonNull = previousOutput.stream() + .filter(EsqlQueryGenerator::fieldCanBeUsed) + .filter(x -> x.type().equals("null") == false) + .collect(Collectors.toList()); if (nonNull.isEmpty()) { return ""; // cannot do any stats, just skip } @@ -402,6 +436,9 @@ private static String stats(List previousOutput) { name = randomIdentifier(); } else { name = randomName(previousOutput); + if (name == null) { + name = randomIdentifier(); + } } String expression = agg(nonNull); if (i > 0) { @@ -433,6 +470,9 @@ private static String agg(List previousOutput) { } // all types name = randomName(previousOutput); + if (name == null) { + return "count(*)"; + } return switch (randomIntBetween(0, 2)) { case 0 -> "count(*)"; case 1 -> "count(" + name + ")"; @@ -529,4 +569,12 @@ private static String randomIdentifier() { return randomAlphaOfLength(randomIntBetween(8, 12)); } + private static boolean fieldCanBeUsed(Column field) { + return ( + // https://github.com/elastic/elasticsearch/issues/121741 + field.name().equals("") + // this is a known pathological case, no need to test it for now + || field.name().equals("")) == false; + } + }