|
60 | 60 | import org.elasticsearch.index.mapper.blockloader.BlockLoaderFunctionConfig; |
61 | 61 | import org.elasticsearch.index.shard.ShardId; |
62 | 62 | import org.elasticsearch.license.XPackLicenseState; |
| 63 | +import org.elasticsearch.logging.LogManager; |
| 64 | +import org.elasticsearch.logging.Logger; |
63 | 65 | import org.elasticsearch.search.SearchService; |
64 | 66 | import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils; |
65 | 67 | import org.elasticsearch.search.crossproject.CrossProjectModeDecider; |
@@ -225,6 +227,8 @@ public final class EsqlTestUtils { |
225 | 227 | public static final Literal FIVE = new Literal(Source.EMPTY, 5, DataType.INTEGER); |
226 | 228 | public static final Literal SIX = new Literal(Source.EMPTY, 6, DataType.INTEGER); |
227 | 229 |
|
| 230 | + private static final Logger LOGGER = LogManager.getLogger(EsqlTestUtils.class); |
| 231 | + |
228 | 232 | public static Equals equalsOf(Expression left, Expression right) { |
229 | 233 | return new Equals(EMPTY, left, right, null); |
230 | 234 | } |
@@ -1375,4 +1379,100 @@ private static String unquote(String index, int numOfQuotes) { |
1375 | 1379 | return index.substring(numOfQuotes, index.length() - numOfQuotes); |
1376 | 1380 | } |
1377 | 1381 |
|
| 1382 | + /** |
| 1383 | + * Convert index patterns and subqueries in FROM commands to use remote indices. |
| 1384 | + */ |
| 1385 | + public static String convertSubqueryToRemoteIndices(String testQuery) { |
| 1386 | + String query = testQuery; |
| 1387 | + // find the main from command, ignoring pipes inside subqueries |
| 1388 | + List<String> mainFromCommandAndTheRest = splitIgnoringParentheses(query, "|"); |
| 1389 | + String mainFrom = mainFromCommandAndTheRest.get(0).strip(); |
| 1390 | + List<String> theRest = mainFromCommandAndTheRest.size() > 1 |
| 1391 | + ? mainFromCommandAndTheRest.subList(1, mainFromCommandAndTheRest.size()) |
| 1392 | + : List.of(); |
| 1393 | + // check for metadata in the main from command |
| 1394 | + List<String> mainFromCommandWithMetadata = splitIgnoringParentheses(mainFrom, "metadata"); |
| 1395 | + mainFrom = mainFromCommandWithMetadata.get(0).strip(); |
| 1396 | + // if there is metadata, we need to add it back later |
| 1397 | + String metadata = mainFromCommandWithMetadata.size() > 1 ? " metadata " + mainFromCommandWithMetadata.get(1) : ""; |
| 1398 | + // the main from command could be a comma separated list of index patterns, and subqueries |
| 1399 | + List<String> indexPatternsAndSubqueries = splitIgnoringParentheses(mainFrom, ","); |
| 1400 | + List<String> transformed = new ArrayList<>(); |
| 1401 | + for (String indexPatternOrSubquery : indexPatternsAndSubqueries) { |
| 1402 | + // remove the from keyword if it's there |
| 1403 | + indexPatternOrSubquery = indexPatternOrSubquery.strip(); |
| 1404 | + if (indexPatternOrSubquery.toLowerCase(Locale.ROOT).startsWith("from ")) { |
| 1405 | + indexPatternOrSubquery = indexPatternOrSubquery.strip().substring(5); |
| 1406 | + } |
| 1407 | + // substitute the index patterns or subquery with remote index patterns |
| 1408 | + if (isSubquery(indexPatternOrSubquery)) { |
| 1409 | + // it's a subquery, we need to process it recursively |
| 1410 | + String subquery = indexPatternOrSubquery.strip().substring(1, indexPatternOrSubquery.length() - 1); |
| 1411 | + String transformedSubquery = convertSubqueryToRemoteIndices(subquery); |
| 1412 | + transformed.add("(" + transformedSubquery + ")"); |
| 1413 | + } else { |
| 1414 | + // It's an index pattern, we need to convert it to remote index pattern. |
| 1415 | + String remoteIndex = unquoteAndRequoteAsRemote(indexPatternOrSubquery, false); |
| 1416 | + transformed.add(remoteIndex); |
| 1417 | + } |
| 1418 | + } |
| 1419 | + // rebuild from command from transformed index patterns and subqueries |
| 1420 | + String transformedFrom = "FROM " + String.join(", ", transformed) + metadata; |
| 1421 | + // rebuild the whole query |
| 1422 | + mainFromCommandAndTheRest.set(0, transformedFrom); |
| 1423 | + testQuery = String.join(" | ", mainFromCommandAndTheRest); |
| 1424 | + |
| 1425 | + LOGGER.trace("Transform query: \nFROM: {}\nTO: {}", query, testQuery); |
| 1426 | + return testQuery; |
| 1427 | + } |
| 1428 | + |
| 1429 | + /** |
| 1430 | + * Checks if the given string is a subquery (enclosed in parentheses). |
| 1431 | + */ |
| 1432 | + private static boolean isSubquery(String indexPatternOrSubquery) { |
| 1433 | + String trimmed = indexPatternOrSubquery.strip(); |
| 1434 | + return trimmed.startsWith("(") && trimmed.endsWith(")"); |
| 1435 | + } |
| 1436 | + |
| 1437 | + /** |
| 1438 | + * Splits the input string by the given delimiter, ignoring delimiters inside parentheses. |
| 1439 | + */ |
| 1440 | + public static List<String> splitIgnoringParentheses(String input, String delimiter) { |
| 1441 | + List<String> results = new ArrayList<>(); |
| 1442 | + if (input == null || input.isEmpty()) return results; |
| 1443 | + |
| 1444 | + int depth = 0; // parentheses nesting |
| 1445 | + int lastSplit = 0; |
| 1446 | + int delimiterLength = delimiter.length(); |
| 1447 | + |
| 1448 | + for (int i = 0; i <= input.length() - delimiterLength; i++) { |
| 1449 | + char c = input.charAt(i); |
| 1450 | + |
| 1451 | + if (c == '(') { |
| 1452 | + depth++; |
| 1453 | + } else if (c == ')') { |
| 1454 | + if (depth > 0) depth--; |
| 1455 | + } |
| 1456 | + |
| 1457 | + // check delimiter only outside parentheses |
| 1458 | + if (depth == 0) { |
| 1459 | + boolean match; |
| 1460 | + if (delimiter.length() == 1) { |
| 1461 | + match = c == delimiter.charAt(0); |
| 1462 | + } else { |
| 1463 | + match = input.regionMatches(true, i, delimiter, 0, delimiterLength); |
| 1464 | + } |
| 1465 | + |
| 1466 | + if (match) { |
| 1467 | + results.add(input.substring(lastSplit, i).trim()); |
| 1468 | + lastSplit = i + delimiterLength; |
| 1469 | + i += delimiterLength - 1; // skip the delimiter |
| 1470 | + } |
| 1471 | + } |
| 1472 | + } |
| 1473 | + // add remaining part |
| 1474 | + results.add(input.substring(lastSplit).trim()); |
| 1475 | + |
| 1476 | + return results; |
| 1477 | + } |
1378 | 1478 | } |
0 commit comments