Skip to content

Commit 5841f08

Browse files
committed
Refine containsHead to only check main pipeline, not subqueries
Change from recursive all-children traversal to iterative first-child chain walk. This ensures head commands inside join subqueries or nested structures do not incorrectly suppress fetch_size injection on the outer query. Signed-off-by: ahkcs <austinhkcs@gmail.com> Signed-off-by: Kai Huang <ahkcs@amazon.com>
1 parent a6e1216 commit 5841f08

1 file changed

Lines changed: 14 additions & 8 deletions

File tree

ppl/src/main/java/org/opensearch/sql/ppl/parser/AstStatementBuilder.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
import static org.opensearch.sql.executor.QueryType.PPL;
99

1010
import com.google.common.collect.ImmutableList;
11+
import java.util.List;
1112
import lombok.Builder;
1213
import lombok.Data;
1314
import lombok.RequiredArgsConstructor;
15+
import org.opensearch.sql.ast.Node;
1416
import org.opensearch.sql.ast.expression.AllFields;
1517
import org.opensearch.sql.ast.statement.Explain;
1618
import org.opensearch.sql.ast.statement.Query;
@@ -70,18 +72,22 @@ public static class StatementBuilderContext {
7072
}
7173

7274
/**
73-
* Recursively checks if the AST contains a {@link Head} node. When the user's query already
74-
* includes an explicit {@code head} command, we should not inject an additional Head for
75-
* fetch_size so that the user's explicit limit takes precedence.
75+
* Checks if the main pipeline contains a {@link Head} node by walking the first-child chain. Only
76+
* the main pipeline is checked — subqueries in joins or nested structures are not traversed. When
77+
* the user's query already includes an explicit {@code head} command, we should not inject an
78+
* additional Head for fetch_size so that the user's explicit limit takes precedence.
7679
*/
7780
private boolean containsHead(UnresolvedPlan plan) {
78-
if (plan instanceof Head) {
79-
return true;
80-
}
81-
for (var child : plan.getChild()) {
82-
if (child instanceof UnresolvedPlan && containsHead((UnresolvedPlan) child)) {
81+
UnresolvedPlan current = plan;
82+
while (current != null) {
83+
if (current instanceof Head) {
8384
return true;
8485
}
86+
List<? extends Node> children = current.getChild();
87+
if (children.isEmpty() || !(children.get(0) instanceof UnresolvedPlan)) {
88+
break;
89+
}
90+
current = (UnresolvedPlan) children.get(0);
8591
}
8692
return false;
8793
}

0 commit comments

Comments
 (0)