yashmayya opened a new pull request, #18925:
URL: https://github.com/apache/pinot/pull/18925

   ## Problem
   
   Querying a table with no routable segments (an empty table, or one whose 
segments were all pruned by the broker) under the multi-stage **physical 
optimizer** (`usePhysicalOptimizer=true`) fails at plan time with:
   
   ```
   QueryException 150 (SQL_PARSING): Error composing query plan: No routing 
entry for offline or realtime type
   ```
   
   The single-stage engine and the legacy multi-stage path both return an empty 
result set for such a query — only the physical-optimizer path throws.
   
   ## Root cause
   
   `LeafStageWorkerAssignmentRule` throws a hard `Preconditions.checkState` 
when a table scan has no active table types. For an empty table, 
`BaseBrokerRoutingManager#getRoutingTable` returns a **non-null** 
`RoutingTable` with an **empty** server-to-segments map (the routing entry 
exists, there are just no segments), so `getActiveTableTypes()` is empty and 
the precondition trips.
   
   The legacy `WorkerManager` has no such precondition — it assigns zero 
workers, and the empty leaf stage is short-circuited to an empty result on the 
broker (added in #18538). That short-circuit was only wired to the legacy path; 
the physical-optimizer path was left with an explicit TODO in 
`PinotDispatchPlanner#finalizeDispatchableSubPlan`.
   
   ## Fix
   
   - **`LeafStageWorkerAssignmentRule`** — assign zero workers to an empty leaf 
instead of throwing, mirroring the legacy engine.
   - **`WorkerExchangeAssignmentRule` / `PlanFragmentAndMailboxAssignment`** — 
tolerate zero-worker (empty) leaf stages so the plan reaches the reduce-stage 
rewrite instead of tripping worker-count assumptions (the singleton gather and 
the mailbox wiring).
   - **`PinotDispatchPlanner`** — track empty leaf stages for the 
physical-optimizer path (implementing the pre-existing TODO), so the shared 
empty-leaf short-circuit fires and the broker returns an empty result **without 
dispatching to any server**.
   
   ### Scope: partially-empty multi-table queries
   
   A query that combines an empty table with a non-empty table (a join or union 
where one side has no routable segments) is **not yet supported** by the 
physical optimizer: a downstream join/set-op/aggregate derives its worker set 
from its inputs, so a zero-worker empty branch can zero out a whole stage and 
silently drop rows. Rather than return wrong results, these now **fail fast 
with a clear, actionable error** suggesting `usePhysicalOptimizer=false` (which 
handles them correctly). They already failed with the opaque error above before 
this change, so this is not a regression. Making them fully work is left as a 
follow-up.
   
   ## Testing
   
   - `LeafStageWorkerAssignmentRuleTest` — empty-table worker assignment 
returns zero workers.
   - `EmptyTablePlanTest` (new) — planner-level coverage: empty single-table 
short-circuit (`SELECT *` / filter / `COUNT(*)` / `GROUP BY`), all-empty 
multi-leaf union short-circuit, and partially-empty fail-fast.
   - `MultiStageEngineIntegrationTest` — end-to-end short-circuit under the 
physical optimizer (empty result, 0 servers queried, correct schema/types for 
aggregates), plus partially-empty fail-fast with the legacy engine verified as 
a working fallback.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to