Skip to content

[HDX-5079] Render formulas in the composed metric query - #2908

Open
wrn14897 wants to merge 3 commits into
mainfrom
warren/HDX-5079-render-formulas
Open

[HDX-5079] Render formulas in the composed metric query#2908
wrn14897 wants to merge 3 commits into
mainfrom
warren/HDX-5079-render-formulas

Conversation

@wrn14897

Copy link
Copy Markdown
Member

Summary

Renders metric formulas (HDX-5078's formulas config) in the composed multi-series metric query (HDX-5077), so a derived series like A / (A + B + C) * 100 is computed by ClickHouse as part of the single composed query instead of not rendering at all.

  • compileFormulaAst (core/formula.ts): compiles the validated letter-ref AST into a SQL expression over per-series value expressions. Never splices user text into SQL — only the parsed/validated AST is walked.
  • Missing-data semantics match the existing ratio projection: a series ref compiles to coalesce(<pivot>, 0) (a missing operand counts as 0, so a zero-error group reads 0%, not N/A), and every division denominator is wrapped in nullif(..., 0) (zero or missing denominator → NULL → rendered gap, never 0 or an error).
  • Projection order (renderMultiSeriesMetricChartConfig): operand value columns first in select order, then formula columns in formulas order, ahead of the group/bucket passthrough columns — preserving the useChartNumberFormats positional meta contract. showOperandSeries: false drops the operand columns so only the formula column(s) are returned.
  • Routing: a single-series metric chart with a formula (e.g. A * 100) now routes through the composed path (single-branch union pivot). Per-series branches strip formulas to avoid recursion.
  • Precedence: formulas supersede seriesReturnType: 'ratio' (the two are mutually exclusive in the editor; the renderer stays deterministic on a hand-built config carrying both).
  • builderToRawSql: formula configs are rejected from "convert to SQL" with a clear message (same limitation as multi-series metric charts).
  • Formulas are validated at render time with the structured validator; an invalid persisted expression throws a descriptive error rather than a ClickHouse error.

Alerts on formula tiles work with no changes since they query through queryChartConfigrenderChartConfig.

Testing

  • make ci-lint, make ci-unit pass.
  • New unit tests: compileFormulaAst (precedence, nested divisions, unary minus, HDX-4938 example) and SQL snapshot tests for the formula projection (grouped, hidden operands, single-series routing, formula-vs-ratio precedence, alias collision suffixing, invalid-formula error).
  • New integration tests in queryChartConfig.int.test.ts (all passing against the docker ClickHouse, with the HDX-5076/5077 regression baseline unchanged):
    • HDX-4938 motivating example A / (A + B + C) * 100
    • Division by zero / missing denominator → gap, not 0 or error (pinned against the same fixture as the seriesReturnType: 'ratio' test for drop-in parity)
    • Formula over mixed gauge + sum (increase) operands across tables
    • Grouped formula computed per (bucket, group) row
    • Meta contract with operand series shown vs hidden
    • Number- and table-shape formulas

How to test on Vercel preview

N/A — query-rendering change in common-utils; the chart editor UI for formulas lands in HDX-5080.

References

Compile the validated formula AST (HDX-5078) into the final SELECT
projection over the pivoted per-series columns produced by the composed
multi-series metric query (HDX-5077).

- compileFormulaAst: letter refs resolve to the pivot expressions with
  ratio-consistent semantics (missing operand -> 0, division by zero or
  missing denominator -> NULL, rendered as a gap)
- Formula columns append after the operand value columns (select order),
  preserving the useChartNumberFormats positional meta contract;
  showOperandSeries: false emits only the formula column(s)
- Single-series metric charts with a formula route through the composed
  path; formulas take precedence over seriesReturnType: 'ratio'
- builderToRawSql rejects formula configs (same limitation as
  multi-series metric charts)
@wrn14897 wrn14897 added the ai-generated AI-generated content; review carefully before merging. label Aug 13, 2026
@changeset-bot

changeset-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8d9e37e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@hyperdx/common-utils Minor
@hyperdx/api Patch
@hyperdx/app Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview Aug 14, 2026 4:58am
hyperdx-storybook Ready Ready Preview Aug 14, 2026 4:58am

Request Review

@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR renders validated metric formulas in composed ClickHouse queries while preserving operand ordering and missing-data semantics.

  • Compiles formula ASTs into derived SQL projections.
  • Routes single-series formula charts through the composed-query path.
  • Supports hidden operands, grouped and ungrouped charts, and formula-aware raw-SQL conversion errors.
  • Escapes composed-query formula, operand, and ratio output aliases as ClickHouse identifiers.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/common-utils/src/core/formula.ts Adds an AST-to-SQL compiler with coalesced operands and guarded division denominators.
packages/common-utils/src/core/renderChartConfig.ts Integrates formulas into composed metric projections and correctly completes the previously requested alias-escaping fix.
packages/common-utils/src/core/builderToRawSql.ts Rejects formula metric configurations that cannot be represented by the raw-SQL conversion path.
packages/common-utils/src/tests/renderChartConfig.test.ts Covers formula projection, routing, validation, collisions, precedence, and escaped aliases.
packages/common-utils/src/tests/queryChartConfig.int.test.ts Exercises formula execution across missing data, grouping, mixed metric types, and chart shapes.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Config[Metric chart config] --> Validate[Validate formula AST]
  Validate --> Branches[Render one query branch per operand]
  Branches --> Union[UNION ALL with series index]
  Union --> Pivot[Pivot operand values]
  Pivot --> Formula[Compile formula projection]
  Formula --> Result[Formula and optional operand columns]
Loading

Reviews (3): Last reviewed commit: "chore: drop ticket references from formu..." | Re-trigger Greptile

Comment thread packages/common-utils/src/core/renderChartConfig.ts Outdated
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 291 passed • 1 skipped • 846s

Status Count
✅ Passed 291
❌ Failed 0
⚠️ Flaky 1
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@wrn14897
wrn14897 marked this pull request as ready for review August 14, 2026 04:22
@github-actions github-actions Bot added the review/tier-2 Low risk — AI review + quick human skim label Aug 14, 2026
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

🔵 Tier 2 — Low Risk

Small, isolated change with no API route or data model modifications.

Why this tier:

  • Standard feature/fix — introduces new logic or modifies core functionality

Additional context: touches the query rendering engine lightly (130 lines, under the 150-line bar for Tier 4)

Review process: AI review + quick human skim (target: 5–15 min). Reviewer validates AI assessment and checks for domain-specific concerns.
SLA: Resolve within 4 business hours.

Stats
  • Production files changed: 3
  • Production lines changed: 176 (+ 523 in test files, excluded from tier calculation)
  • Branch: warren/HDX-5079-render-formulas
  • Author: wrn14897

To override this classification, remove the review/tier-2 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

A formula or series alias containing a double quote terminated the
double-quoted identifier early (AS "bad"name") and failed the query
with a ClickHouse syntax error. Escape by doubling the quote at
SQL-emission time across all composed-projection sites (formula,
operand, and ratio columns); collision dedup and result meta keep the
raw name.

Addresses greptile P1 review comment.
@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Deep Review

Rendering metric formulas (A / (A + B + C) * 100) into the composed multi-series ClickHouse metric query. I read every changed line in formula.ts, renderChartConfig.ts, and builderToRawSql.ts, plus the supporting parser/validator and config schema. The core is sound: compileFormulaAst walks only the validated AST (never splicing raw expression text), numeric literals are digits-only, validateFormula runs before compilation, and quotedColumnName now escapes embedded double quotes at every emission site inside renderMultiSeriesMetricChartConfig (formula, operand, ratio, and plain paths). Single-series charts without a formula are unaffected by the new routing, and the value-columns-first meta ordering that useChartNumberFormats depends on is preserved.

✅ No critical issues found. No P0/P1 defects; the diff is safe to merge. One test gap and a couple of small maintainability nits below.

🟡 P2 — recommended

  • packages/common-utils/src/core/builderToRawSql.ts:100 — The new branch that rejects formula-bearing metric configs from raw-SQL conversion (including single-series configs with formulas, which previously converted) and its distinct error message are untested; a refactor flipping the ternary or dropping the formulas clause passes CI silently.
    • Fix: Add a builderToRawSql test asserting a single-series metric config carrying formulas returns isError: true with the formula-specific message.
    • testing
🔵 P3 nitpicks (2)
  • packages/common-utils/src/core/renderChartConfig.ts:2473 — The per-series value-column projection (valueExprFor(splitIdx) AS quotedColumnName(...)) is emitted byte-identically in both the hasFormulas operand branch and the plain else branch, so a future change to value-column emission must be made twice or the two silently diverge.
    • Fix: Extract a shared closure that both branches call to push the ordered value columns.
    • maintainability
  • packages/common-utils/src/core/builderToRawSql.ts:102 — The formula-presence check (config.formulas?.length ?? 0) > 0 is re-implemented inline (twice) rather than reusing the exported hasMetricFormulas helper, duplicating the definition of "what counts as a formula chart" across modules.
    • Fix: Import and call hasMetricFormulas in place of the inline expression.
    • maintainability

Reviewers (8 dispatched): correctness, security, adversarial, testing, maintainability, kieran-typescript, api-contract, learnings-researcher. Graded findings reflect the testing and maintainability reviewers plus direct orchestrator analysis of the correctness, security, and API-contract dimensions (all clean); the learnings researcher found no prior docs/solutions/ guidance on ClickHouse SQL generation or the composed-metric query.

Testing gaps:

  • The number-chart (no passthrough columns) formula projection with implicit global aggregation is exercised only by the live-ClickHouse integration test, not by a fast unit-level SQL-shape assertion in renderChartConfig.test.ts.
  • Pre-existing / out of scope: the general renderSelect path (renderChartConfig.ts:817) still interpolates a user alias into an AS "..." identifier without escaping — the natural home for the new quotedColumnName helper. Not introduced by this diff; worth a follow-up.

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

Labels

ai-generated AI-generated content; review carefully before merging. review/tier-2 Low risk — AI review + quick human skim

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant