Problem
PlanDataInjector.injectPlanData walks every operator in the tree against every registered injector:
for (injector <- injectors if injector.canInject(op)) { ... }
That is N operators x M injectors canInject calls, even though most operators in any tree are non-scan and match no injector.
Proposed fix
Add def opStructCase: Operator.OpStructCase to the PlanDataInjector trait and build a Map[Operator.OpStructCase, PlanDataInjector]. Look up by op.getOpStructCase (O(1)) and then run a single canInject confirm (which may still inspect detail fields). Non-scan operators skip the iteration entirely. Pure performance change -- no behavior difference.
Relationship to the Delta integration
This is an independent micro-optimization, not Delta-specific. It is being extracted out of the in-progress Delta Lake contrib integration branch (which registers a third injector) purely to keep that PR focused; there is no functional dependency. A PR will follow shortly.
Problem
PlanDataInjector.injectPlanDatawalks every operator in the tree against every registered injector:That is N operators x M injectors
canInjectcalls, even though most operators in any tree are non-scan and match no injector.Proposed fix
Add
def opStructCase: Operator.OpStructCaseto thePlanDataInjectortrait and build aMap[Operator.OpStructCase, PlanDataInjector]. Look up byop.getOpStructCase(O(1)) and then run a singlecanInjectconfirm (which may still inspect detail fields). Non-scan operators skip the iteration entirely. Pure performance change -- no behavior difference.Relationship to the Delta integration
This is an independent micro-optimization, not Delta-specific. It is being extracted out of the in-progress Delta Lake contrib integration branch (which registers a third injector) purely to keep that PR focused; there is no functional dependency. A PR will follow shortly.