Skip to content

Fix explicit tableOptions hint handling in PinotImplicitTableHintRule#19003

Open
yashmayya wants to merge 1 commit into
apache:masterfrom
yashmayya:fix-implicit-table-hint-explicit-options
Open

Fix explicit tableOptions hint handling in PinotImplicitTableHintRule#19003
yashmayya wants to merge 1 commit into
apache:masterfrom
yashmayya:fix-implicit-table-hint-explicit-options

Conversation

@yashmayya

Copy link
Copy Markdown
Contributor

This fixes two bugs in PinotImplicitTableHintRule (the rule that infers tableOptions partition hints when inferPartitionHint is enabled via the pinot.broker.multistage.infer.partition.hint broker config or the inferPartitionHint query option):

1. Explicit non-partition hint options (e.g. is_replicated) were dropped during inference

withNewTableOptions() rebuilt the tableOptions hint with only the partition options (partition_key / partition_function / partition_size / partition_parallelism), silently discarding any other explicitly supplied options — notably is_replicated='true'.

As a result, a replicated local join like:

SELECT /*+ joinOptions(left_distribution_type='local', right_distribution_type='local') */ a.col1, b.col2
FROM a
JOIN b /*+ tableOptions(is_replicated='true') */ ON a.col1 = b.col1

fails during worker assignment with Found multiple local exchanges in the children when partition hint inference is enabled, because the replicated leaf loses its is_replicated flag and its SINGLETON send is counted as a second local exchange in WorkerManager.

The rebuilt hint's kv-options are now seeded from the explicit hint so that options not modeled by TableOptions are carried over, and the merged (inferred + explicitly overridden) partition options overwrite the explicitly supplied ones.

2. Explicit partition_key / partition_function overrides of inferred options were never applied

overridePartitionKey() and overridePartitionFunction() did a double map lookup — kvOptions.get(kvOptions.get(PARTITION_KEY)) — which looks up the option value as a key and therefore always resolves to null, so partial explicit overrides of the inferred partition key/function were silently ignored (the documented contract is "any explicit hint will override the implicit hint").

Note on behavior: this is a user-visible behavior change for deployments running with partition hint inference enabled — a partial tableOptions hint carrying an incorrect partition_key / partition_function was previously ignored (the query planned with the inferred values), and will now be honored and fail planning with a validation error (e.g. Partition key: col1 does not match partition column: col2) until the hint is corrected or removed.

Tests

Planner regression tests (all fail without the fix):

  • JoinPlans.json: the replicated local join above with SET inferPartitionHint=true, plus a variant where the hint also carries partition_parallelism, both asserting the plan and successful worker assignment.
  • PinotHintablePlans.json: partial-hint override cases with SET inferPartitionHint=true — a wrong partition_key / partition_function now fails planning with the mismatch error, and a matching partition_key plans successfully.

@yashmayya yashmayya added bug Something is not working as expected multi-stage Related to the multi-stage query engine labels Jul 16, 2026
@yashmayya
yashmayya requested review from Jackie-Jiang and gortiz July 16, 2026 20:46

@Jackie-Jiang Jackie-Jiang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel the main issue is that TableOptions do not maintain all available options. When is_replicated is set, we shouldn't override partition related options

*/
private static ImmutableTableOptions overridePartitionKey(ImmutableTableOptions base, TableScan tableScan,
Map<String, String> kvOptions) {
String partitionKey = kvOptions.get(kvOptions.get(PinotHintOptions.TableHintOptions.PARTITION_KEY));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol

*/
private static ImmutableTableOptions overridePartitionFunction(ImmutableTableOptions base,
TableScan tableScan, Map<String, String> kvOptions) {
String partitionFunction = kvOptions.get(kvOptions.get(PinotHintOptions.TableHintOptions.PARTITION_FUNCTION));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lol + 1

@codecov-commenter

codecov-commenter commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.26%. Comparing base (3718bc3) to head (42f24a1).
⚠️ Report is 91 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master   #19003      +/-   ##
============================================
+ Coverage     64.79%   65.26%   +0.46%     
- Complexity     1318     1405      +87     
============================================
  Files          3393     3419      +26     
  Lines        211566   214704    +3138     
  Branches      33285    33932     +647     
============================================
+ Hits         137091   140129    +3038     
+ Misses        63398    63275     -123     
- Partials      11077    11300     +223     
Flag Coverage Δ
custom-integration1 100.00% <ø> (ø)
integration 100.00% <ø> (ø)
integration1 100.00% <ø> (ø)
integration2 0.00% <ø> (ø)
java-21 65.26% <100.00%> (+0.46%) ⬆️
temurin 65.26% <100.00%> (+0.46%) ⬆️
unittests 65.26% <100.00%> (+0.46%) ⬆️
unittests1 56.91% <100.00%> (-0.07%) ⬇️
unittests2 37.74% <0.00%> (+0.58%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

bug Something is not working as expected multi-stage Related to the multi-stage query engine

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants