|
121 | 121 | import org.opensearch.sql.ast.tree.Regex; |
122 | 122 | import org.opensearch.sql.ast.tree.Relation; |
123 | 123 | import org.opensearch.sql.ast.tree.Rename; |
| 124 | +import org.opensearch.sql.ast.tree.Replace; |
| 125 | +import org.opensearch.sql.ast.tree.ReplacePair; |
124 | 126 | import org.opensearch.sql.ast.tree.Rex; |
125 | 127 | import org.opensearch.sql.ast.tree.SPath; |
126 | 128 | import org.opensearch.sql.ast.tree.Search; |
@@ -2455,6 +2457,51 @@ public RelNode visitValues(Values values, CalcitePlanContext context) { |
2455 | 2457 | } |
2456 | 2458 | } |
2457 | 2459 |
|
| 2460 | + @Override |
| 2461 | + public RelNode visitReplace(Replace node, CalcitePlanContext context) { |
| 2462 | + visitChildren(node, context); |
| 2463 | + |
| 2464 | + List<String> fieldNames = context.relBuilder.peek().getRowType().getFieldNames(); |
| 2465 | + |
| 2466 | + // Create a set of field names to replace for quick lookup |
| 2467 | + Set<String> fieldsToReplace = |
| 2468 | + node.getFieldList().stream().map(f -> f.getField().toString()).collect(Collectors.toSet()); |
| 2469 | + |
| 2470 | + // Validate that all fields to replace exist by calling field() on each |
| 2471 | + // This leverages relBuilder.field()'s built-in validation which throws |
| 2472 | + // IllegalArgumentException if any field doesn't exist |
| 2473 | + for (String fieldToReplace : fieldsToReplace) { |
| 2474 | + context.relBuilder.field(fieldToReplace); |
| 2475 | + } |
| 2476 | + |
| 2477 | + List<RexNode> projectList = new ArrayList<>(); |
| 2478 | + |
| 2479 | + // Project all fields, replacing specified ones in-place |
| 2480 | + for (String fieldName : fieldNames) { |
| 2481 | + if (fieldsToReplace.contains(fieldName)) { |
| 2482 | + // Replace this field in-place with all pattern/replacement pairs applied sequentially |
| 2483 | + RexNode fieldRef = context.relBuilder.field(fieldName); |
| 2484 | + |
| 2485 | + // Apply all replacement pairs sequentially (nested REPLACE calls) |
| 2486 | + for (ReplacePair pair : node.getReplacePairs()) { |
| 2487 | + RexNode patternNode = rexVisitor.analyze(pair.getPattern(), context); |
| 2488 | + RexNode replacementNode = rexVisitor.analyze(pair.getReplacement(), context); |
| 2489 | + fieldRef = |
| 2490 | + context.relBuilder.call( |
| 2491 | + SqlStdOperatorTable.REPLACE, fieldRef, patternNode, replacementNode); |
| 2492 | + } |
| 2493 | + |
| 2494 | + projectList.add(fieldRef); |
| 2495 | + } else { |
| 2496 | + // Keep original field unchanged |
| 2497 | + projectList.add(context.relBuilder.field(fieldName)); |
| 2498 | + } |
| 2499 | + } |
| 2500 | + |
| 2501 | + context.relBuilder.project(projectList, fieldNames); |
| 2502 | + return context.relBuilder.peek(); |
| 2503 | + } |
| 2504 | + |
2458 | 2505 | private void buildParseRelNode(Parse node, CalcitePlanContext context) { |
2459 | 2506 | RexNode sourceField = rexVisitor.analyze(node.getSourceField(), context); |
2460 | 2507 | ParseMethod parseMethod = node.getParseMethod(); |
|
0 commit comments