Skip to content

[Analytics-Engine] Seed row counts for tables referenced only inside a subquery - #22878

Open
LantaoJin wants to merge 1 commit into
opensearch-project:mainfrom
LantaoJin:bugfix/subquery-rowcount-seeding
Open

[Analytics-Engine] Seed row counts for tables referenced only inside a subquery#22878
LantaoJin wants to merge 1 commit into
opensearch-project:mainfrom
LantaoJin:bugfix/subquery-rowcount-seeding

Conversation

@LantaoJin

@LantaoJin LantaoJin commented Aug 29, 2026

Copy link
Copy Markdown
Member

Description

Row counts are not seeded for tables referenced only inside a subquery, so those scans keep Calcite's default estimate. This is not MPP related. see #22877 for details.

Related Issues

Resolves #22877

Check List

  • Functionality includes testing.
  • API changes companion pull request created, if applicable.
  • Public documentation issue/PR created, if applicable.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

Signed-off-by: Lantao Jin <ltjin@amazon.com>
@LantaoJin
LantaoJin requested a review from a team as a code owner August 29, 2026 08:59
@github-actions github-actions Bot added bug Something isn't working Plugins labels Aug 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

🧪 PR contains tests
🔒 No security concerns identified
✅ No TODO sections
🔀 No multiple PR themes
⚡ No major issues detected

@github-actions

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Ensure subquery expressions are reliably visited

RelNode.accept(RexShuttle) only visits RexNodes at the current node level and does
not recurse into inputs, but here inputs are already recursed via getInputs().
However, a subquery nested inside another subquery's RexSubQuery.rel will only be
found because collectTableScans recurses into subQuery.rel — which is correct. But
note that RelNode.accept(RexShuttle) behavior depends on the node type; some
RelNodes (e.g. TableScan) return this without visiting expressions. Consider
explicitly extracting RexNodes via a RelShuttle or iterating
RelOptUtil.InputFinder-style over each node's expressions to guarantee subqueries in
Project/Filter are visited consistently.

sandbox/plugins/analytics-engine/src/main/java/org/opensearch/analytics/exec/IndexRowCountFetcher.java [106-112]

 for (RelNode input : node.getInputs()) {
     collectTableScans(input, out);
 }
-// A subquery is not an input — it hangs off a RexNode (a Filter condition or a Project expression)
-// as a RexSubQuery, and this fetcher runs BEFORE decorrelation turns it into a join. Walking only
-// getInputs() therefore misses every table that appears solely inside a subquery, and each of those
-// scans then falls back to Calcite's default row count. That default is small, so the estimates
-// derived from it collapse: filters and aggregates above such a scan come out at a row or two, which
-// makes a large build look tiny and lets plan choices be made on a number with no basis in the data.
-node.accept(new RexShuttle() {
+RexShuttle subQueryVisitor = new RexShuttle() {
     @Override
     public RexNode visitSubQuery(RexSubQuery subQuery) {
         collectTableScans(subQuery.rel, out);
         return super.visitSubQuery(subQuery);
     }
-});
+};
+if (node instanceof org.apache.calcite.rel.core.Filter f) {
+    subQueryVisitor.apply(f.getCondition());
+} else if (node instanceof org.apache.calcite.rel.core.Project p) {
+    subQueryVisitor.apply(p.getProjects());
+} else if (node instanceof org.apache.calcite.rel.core.Join j) {
+    subQueryVisitor.apply(j.getCondition());
+} else {
+    node.accept(subQueryVisitor);
+}
Suggestion importance[1-10]: 5

__

Why: The concern about RelNode.accept(RexShuttle) behavior across node types is legitimate — some RelNodes may not visit all their RexNodes uniformly. However, the tests in the PR demonstrate that the current approach works for Filter, Project, and scalar subquery cases, so the impact is moderate and somewhat speculative.

Low

@github-actions

Copy link
Copy Markdown
Contributor

✅ Gradle check result for dc2eaaa: SUCCESS

@codecov

codecov Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.60%. Comparing base (f81134c) to head (dc2eaaa).
⚠️ Report is 20 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main   #22878      +/-   ##
============================================
- Coverage     71.61%   71.60%   -0.02%     
+ Complexity    77315    77308       -7     
============================================
  Files          6170     6170              
  Lines        359671   359671              
  Branches      52450    52450              
============================================
- Hits         257591   257550      -41     
- Misses        81636    81650      +14     
- Partials      20444    20471      +27     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Plugins

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Row counts are not seeded for tables referenced only inside a subquery, so those scans keep Calcite's default estimate

1 participant