Repository navigation
QueryExpr: merge BinaryOp into Compare/Arithmetic (Instance 2 of #220) #245
Description
Summary
Follow-up to #220, scoped out of the PR that resolved Instance 1 (Scalar/Literal, merged as of that PR — PromqlScalar(f64) collapsed into PromqlScalarBridge(Rc<QueryExpr<C>>) wrapping Literal(ScalarValue::Float64(_))).
Instance 2 from #220 remains open:
QueryExpr::BinaryOp { op: BinaryOpKind, lhs, rhs, vector_match }— the relational/vector-tree binary operator node (PromQL binary ops between two vector-shapedQueryExprs, or a vector and a scalar bridge).QueryExpr::Compare { left, op: CompareOpKind, right }andQueryExpr::Arithmetic { op: ArithmeticOpKind, left, right }— the scalar-expression sub-language's own binary comparison/arithmetic nodes.
BinaryOpKind already just wraps ArithmeticOpKind/CompareOpKind (see its doc comment in query_expr.rs), so the same operator vocabulary is expressed through two different node shapes depending on which side of the relational/scalar split the expression sits on — the same duplication pattern Instance 1 had, one level up.
Why this was scoped out of the #220 PR
BinaryOp has real additional data Compare/Arithmetic don't: vector_match: Option<VectorMatch> (PromQL vector-matching semantics — on/ignoring/group_left/group_right), which only makes sense at the relational/vector-tree position. Merging cleanly means either:
Compare/Arithmeticgain an optional vector-match field used only when the node sits at the relational-tree position, orBinaryOpbecomes a thin relational wrapper around aCompare/Arithmeticnode (mirroring thePromqlScalarBridgewrapper pattern Instance 1 introduced for theScalar/Literalsplit), or- some hybrid.
BinaryOp also has a much larger blast radius than PromqlScalar did — roughly 60 call sites across query_expr.rs, canonicalize.rs, resolve.rs, binder.rs, cse.rs, dag_export.rs, both front ends, and every test suite that pattern-matches on it, several with PromQL-specific vector-match assertions that would need care to preserve exactly. That's large enough to want its own dedicated PR rather than folding into the Instance-1 change.
Suggested approach
Given the wrapper pattern that worked for Instance 1, the BinaryOp-as-thin-relational-wrapper direction seems most consistent: keep BinaryOp { op, lhs, rhs, vector_match } at the operator-tree position (it already carries data Compare/Arithmetic don't need), and where op is BinaryOpKind::Arithmetic/BinaryOpKind::Compare, consider whether the operator enum itself (not just the operand types) can be unified with ArithmeticOpKind/CompareOpKind directly rather than wrapped — worth scoping precisely before starting, including whether vector_match-carrying variants (And/Or/Unless/Pow/Atan2, the PromQL-only members of BinaryOpKind with no scalar-IR counterpart) block a full collapse or just need to stay as BinaryOp-only cases.
References
- QueryExpr has duplicate relational/scalar-language shapes for the same semantics (Scalar/Literal, BinaryOp/Compare/Arith) #220 (parent issue — records the observation, no resolution)
- Rename PromQL-specific QueryExpr nodes (and Merge/Distinct/WindowFunc/EvalTime) for clarity #183 / Consider mapping SQL now()/CURRENT_TIMESTAMP (incl. ClickHouse now()) to EvalTime #184 (the QueryExpr node-naming audit that surfaced QueryExpr has duplicate relational/scalar-language shapes for the same semantics (Scalar/Literal, BinaryOp/Compare/Arith) #220)
- Eliminate L2Expr/L3Expr as a separate scalar-expression type; fold into QueryExpr, generic over column-reference state #205 (folded the old separate
Expr<C>scalar tree intoQueryExpr, which is what created both instances of duplication)
Activity
refactor(types): collapse QueryExpr::PromqlScalar into Literal (#220) (…
zzylol commentedon Aug 26, 2026
Related open work
chore: pin asap_sketchlib to planner-pin/0.4.0-plus (63e5655)
fix(executor): top-k heaps keep the old kernel's membership
chore: pin asap_sketchlib to planner-pin/0.4.0-plus (63e5655)
fix(executor): top-k heaps keep the old kernel's membership
fix(executor): DDSketch interpolated quantiles and top-k membership o…
build: pin asap_sketchlib to main 950bb5c
fix: read sketch capabilities and state bytes from asap_sketchlib (#953…