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]
