Skip to content

PromQL: non-literal scalar parameters (topk(scalar(foo), v), quantile(scalar(f), v), limit_ratio(time()%17/17, v)) #120

Open

Description

@zzylol

Summary

topk / bottomk / quantile / limitk / limit_ratio accept a scalar-valued expression as their k / φ / ratio parameter. We require a literal (after constant folding), and reject everything else:

topk(scalar(foo), http_requests)
→ invalid parameter: expected a numeric scalar, got Discriminant(9)

Split out of #109, whose histogram_quantiles half landed separately. The two share the issue title but nothing else.

Corpus impact

11 rejections. Every one needs a scalar subtree, not a number:

topk(scalar(foo), http_requests)
count(bottomk(scalar(foo), http_requests))
quantile without(point)(scalar(foo), data)
quantile_over_time(scalar(up) + 1, {__name__="up"}[1h:1m] @ 1111111)
count(limitk(scalar(foo), http_requests))
count(limit_ratio(scalar(bar), http_requests))
count(limit_ratio(time() % 17/17, http_requests) or …)

num_expr already constant-folds pure arithmetic (10*1024*1024), so what is missing is specifically a parameter that depends on the data or the evaluation time.

Why this is not a small change

The parameter is baked into the intent as a plain value:

AggIntent::TopK      { k: usize,  accuracy }
AggIntent::Quantile  { col, q: f64, accuracy }
QueryExpr::Sample    { kind: SampleKind, … }        // limitk / limit_ratio

and read back out at L4:

SketchQuery::TopK { k: usize }      // crates/sketch
SketchQuery::Quantile { q: f64 }

Making these expressions means:

  1. The IR has to carry a scalar subtree (Box<QueryExpr> / an L3Scalar expression) in each parameter slot.
  2. plan::boundary decides sketch-vs-exact per intent. It keys on AccuracyTarget, not on k, so the decision survives — but the sketch readout (SketchQuery::TopK { k }) needs k at estimate time, which is now a runtime value.
  3. plan::cse dedupes on AggIntent equality; a parameter subtree has to participate in that equality.
  4. Schema derivation names the quantile output column after φ (quantile_0_99). With a non-literal φ there is no name to derive — the same union-compatibility problem histogram_quantiles hit (see the value aliasing in walk_histogram_quantiles).

Point 4 is the interesting one: it suggests the output-column naming convention, not just the parameter type, needs revisiting.

Suggested scope

Probably two steps:

  • Step 1 — allow a scalar parameter that is constant per evaluation (scalar(foo) still varies per step, so this does not cover the corpus; but time()-free folding already works). Low value on its own.
  • Step 2 — a real ScalarExpr parameter slot threaded to the L4 readout. This is the one the corpus needs.

Worth confirming whether an approximate TopK with a runtime k is even wanted: a CMS+heap is sized by ε, not k, so the sketch is buildable — only the readout needs k. That is a point in favour of step 2 being tractable.

Notes

Activity

  1. added
    enhancementNew feature or request
    promqlPromQL front-end lowering (L1→L2)
    on Jul 9, 2026
  2. added
    irPre-ASAP or post-ASAP intermediate representation
    on Aug 26, 2026
  3. zzylol commented on Aug 26, 2026

    @zzylol
    ContributorAuthor

    Related open work

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

    enhancementNew feature or requestirPre-ASAP or post-ASAP intermediate representationpromqlPromQL front-end lowering (L1→L2)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions