Skip to content

QueryExpr has duplicate relational/scalar-language shapes for the same semantics (Scalar/Literal, BinaryOp/Compare/Arith) #220

Description

@milindsrivastava1997

Summary

While auditing QueryExpr node names for #183, found that #205's fold of the old, separate Expr<C> scalar-expression tree into QueryExpr itself left behind pairs of variants that represent the same semantic value/operation, differing only in which tree position they're allowed to appear in — a relational/vector-tree position (pre-existing QueryExpr variants) versus a scalar-sub-language position (the variants folded in by #205).

Instance 1: Scalar(f64) vs Literal(ScalarValue)

  • QueryExpr::Scalar(f64) — a PromQL bare numeric literal / folded constant. Appears only as a BinaryOp operand (<vector> op <scalar>, e.g. up > 1).
  • QueryExpr::Literal(ScalarValue) — a typed scalar constant (Int64/Float64/Utf8/Boolean/Null), used inside the scalar-expression sub-language (Compare/Arith/Case/InList operands, etc.).

Every PromQL scalar value is a f64, so Literal(ScalarValue::Float64(v)) holds exactly the same value Scalar(v) does. The two variants exist side by side with no semantic difference in the value they carry — only in which part of the tree constructs/expects them.

Note from the investigation: output_schema (crates/types/src/pre_asap/query_expr.rs, ~line 1100 and ~1133-1154), canonicalize.rs (~line 64-85), and resolve.rs (~line 87, 348) all currently use the variant tag itself to distinguish "a scalar-bridge leaf that has its own row schema" (Scalar/EvalTime/VectorFromScalar/ScalarFromVector) from "a nested scalar sub-expression with no row schema of its own" (Literal/Column/Compare/…, which return QueryExprError::ScalarHasNoRowSchema). That distinction currently rides entirely on which of these two variants is used.

Instance 2: BinaryOp vs Compare/Arith

  • QueryExpr::BinaryOp{op: BinaryOpKind, lhs, rhs, vector_match} — the relational/vector-tree binary operator node (PromQL binary ops between two vector-shaped QueryExprs, or a vector and a scalar bridge).
  • QueryExpr::Compare{left, op: CompareOp, right} and QueryExpr::Arith{op: ArithOp, left, right} — the scalar-expression sub-language's own binary comparison/arithmetic nodes (used inside Filter.pred, Aggregate.having, Case branches, etc.).

BinaryOpKind literally reuses ArithOp/CompareOp internally (BinaryOpKind::Arith(ArithOp) / BinaryOpKind::Compare(CompareOp), see the doc comment on BinaryOpKind 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.

Same discriminant reliance as Instance 1: canonicalize.rs:83 gives BinaryOp{lhs,rhs} its own recursion arm (vec![lhs.as_mut(), rhs.as_mut()]) while Compare/Arith (canonicalize.rs:86,95) fall into the generic scalar-leaf bucket alongside Literal; resolve.rs:320,335 gives BinaryOp its own real match arm while Compare/Arith (resolve.rs:349,358) sit in that module's catch-all too. Three separate functions currently tell "relational, has real children to recurse into" from "nested scalar leaf" purely by which of these two variants was used.

Why this is worth tracking

Both instances follow the same shape: a relational-tree node and a scalar-sub-language node exist in parallel for what is, value/operator-wise, the same thing — an artifact of #205 unifying the type (Expr<C> folded into QueryExpr<C>) without unifying the shapes that had accumulated on both sides of the original relational/scalar split. Filing this purely to record the observation from the #183/#184 audit; no proposed resolution here.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    canonicalizationCanonical IR normalization and invariantsirPre-ASAP or post-ASAP intermediate representationtracking issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions