andygrove opened a new issue, #5832:
URL: https://github.com/apache/datafusion-comet/issues/5832
### What is the problem the feature request solves?
`docs/source/contributor-guide/adding_a_new_operator.md` never mentions
`equals`, `hashCode`,
`stringArgs`, canonicalization or exchange reuse. A contributor who follows
it end to end has no
reason to know the override exists, let alone that omitting a field from it
produces silent wrong
answers.
The worked examples make this worse rather than neutral. The guide's
`CometFilterExec` and
`CometProjectExec` case classes in Step 2 show only `outputPartitioning`,
`outputOrdering`,
`producedAttributes` and `withNewChildInternal`. The real classes in
`spark/src/main/scala/org/apache/spark/sql/comet/operators.scala` also
override `stringArgs`,
`equals` and `hashCode`. Someone copying either example verbatim gets the
default case-class
`equals`, which pulls `nativeOp`, `originalPlan` and `serializedPlanOpt`
into plan identity. The
guide gives no signal that anything is missing.
We have shipped the opposite defect three times: #5470 (`resultExpressions`
omitted from
`CometHashAggregateExec`), #5824 (`joinType` omitted from all three join
operators, `outer` never
captured onto `CometExplodeExec`), and a third instance found while fixing
that in #5828
(`isNullAwareAntiJoin` on `CometBroadcastHashJoinExec`). All three returned
wrong results on stock
configuration.
### Describe the potential solution
Two changes to `adding_a_new_operator.md`:
1. Complete the `CometFilterExec` example in Step 2 with its real
`stringArgs`, `equals` and
`hashCode` overrides, so the copy-paste path produces a correct operator.
2. Add a short subsection covering the rule itself:
- Every constructor parameter that changes the operator's results must
appear in both `equals`
and `hashCode`.
- `nativeOp`, `originalPlan` and `serializedPlanOpt` are deliberately
excluded, with a sentence
on why. They are per-instance serialization state, and
`CometNativeExec.canonicalizePlans`
nulls `originalPlan` out during canonicalization.
- Getting this wrong does not fail loudly. `ReuseExchangeAndSubquery`
shares a shuffle between
the two plans and the query returns one branch's rows twice, usually
only on inputs where an
unrelated optimizer rule is not already making the two subtrees differ.
- Note that `CometBroadcastExchangeExec` and `CometNativeScanExec` use
the other valid
convention, comparing `originalPlan` in place of the individual fields
and overriding
`doCanonicalize` to match, so a reader who greps for a second example
is not confused by it.
Step 6 (Add Tests) is also worth a line: if the new operator carries any
field beyond its children
and output, it wants an exchange-reuse regression modelled on the ones in
`CometAggregateSuite`,
`CometJoinSuite` and `CometGenerateExecSuite`. Those tests need to defeat
whatever optimizer rule
would otherwise make the two branches differ, which is the part that is easy
to get wrong and
produce a test that passes for the wrong reason.
### Additional context
#5831 tracks the automated guard for the same problem. The two are
complementary. The guard catches
the omission at build time, the guide stops a contributor writing it in the
first place.
--
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]