Add durationLiteralOutOfRange to centralise the int64 nanosecond overflow
check for numeric duration literals, replacing three identical inline
conditions in the grammar.
Add applyUnaryOpToDurationExpr to handle the unary +/- type-switch over
*DurationExpr/*NumberLiteral, collapsing two near-identical grammar
action blocks (offset_duration_expr and duration_expr) into single calls.
Regenerate generated_parser.y.go.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
The `unary_op LEFT_PAREN duration_expr RIGHT_PAREN` production in
`offset_duration_expr` unconditionally asserted `$3.(*DurationExpr)`,
but `duration_expr` can also reduce to a `*NumberLiteral` (e.g. the
literal `0` or `5` inside parentheses). This caused a runtime panic
recoverable only by the top-level recover in parse.go, which surfaced
as an opaque "unexpected error".
Replace the hard type-assertion with a type switch matching the same
pattern used by the `unary_op duration_expr` production in
`duration_expr`: handle `*DurationExpr` (set Wrapped, negate if SUB)
and `*NumberLiteral` (negate value if SUB, range-check, update pos),
with a fallback parse error for unexpected node types.
Regenerate generated_parser.y.go from the updated grammar.
Add regression tests: `foo offset +(5)` and `foo offset -(5)` now
parse correctly as ±5 s offsets.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
Export ErrUnexpected from the parser package so the fuzz test can use
errors.Is to detect when the parser's recover() handler caught a
runtime panic. Previously these were silently swallowed; now
FuzzParseExpr fails on them.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
When a MatrixSelector wraps a non-VectorSelector expression (e.g. `1[5m]`),
the grammar records a parse error but continues. The unchecked type assertions
in setAnchored and setSmoothed then panic. Use safe assertions and emit a
parse error instead.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
Implement least(a, b) and greatest(a, b) as scalar-returning PromQL
functions backed by math.Min and math.Max respectively.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
Rename the `min()` and `max()` duration expression functions to
`least()` and `greatest()` to avoid conflicts with the existing
PromQL aggregate functions `min` and `max`.
Update documentation and tests accordingly.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
min(), max(), step(), and range() in duration expression context were
not guarded by the ExperimentalDurationExpr feature flag, unlike the
binary operators (+, -, *, /, %, ^). Add the missing
experimentalDurationExpr() calls in both offset_duration_expr and
duration_expr grammar rules.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
Fixed a bug in the `PositionRange` method where a panic occurred when `e.RHS` was nil.
Previously, the code checked `if e.RHS == nil` but then immediately
accessed `e.RHS.PositionRange().End`, causing a runtime error.
This commit updates the logic to correctly use `e.LHS.PositionRange().End`
when the RHS is missing.
Fixes: ee7d5158a ("Add step(), min(a,b) and max(a,b) in promql duration expressions")
Found by PostgresPro with the Svace static analyzer.
Signed-off-by: Maksim Korotkov <m.korotkov@postgrespro.ru>
Replace the hardcoded switch over start characters and keyword names with
a map-driven approach. durationKeywordTokens maps lowercase keyword strings
to their token types, and isDurationKeywordStartChar derives the valid start
characters from that map, so adding a new duration keyword only requires one
change instead of three.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
Export parser.Keywords() and add GetDictForFuzzParseExpr() so that
the corpus generator can produce a stable fuzzParseExpr.dict file
derived directly from the PromQL grammar rather than maintained by hand.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
Parser configuration is now per-engine/API/loader and no longer uses package-level flags, so behavior is consistent and tests don't rely on save/restore of global variables.
Signed-off-by: Martin Valiente Ainz <64830185+tinitiuset@users.noreply.github.com>
This implements the TRIM_UPPER (</) and TRIM_LOWER (>/) operators
that allow removing observations below or above a threshold from
a histogram. The implementation zeros out buckets outside the desired
range. It also recalculates the sum, including only bucket counts within
the specified threshold range.
Fixes#14651.
Signed-off-by: sujal shah <sujalshah28092004@gmail.com>
This commit addresses the PR feedback for issue #17615. The previous
implementation could not distinguish between:
- No counter reset hint specified (meaning "don't care")
- counter_reset_hint:unknown explicitly specified (meaning "verify it's unknown")
Changes:
- Added CounterResetHintSet field to parser.SequenceValue to track
whether counter_reset_hint was explicitly specified in the test file
- Modified buildHistogramFromMap to set this flag when the hint is
present in the descriptor map
- Updated newHistogramSequenceValue helper and histogramsSeries
functions to propagate the flag through histogram series creation
- Updated yacc grammar to use the new helper function
- Modified compareNativeHistogram to accept the flag and only compare
hints when explicitly specified
This allows tests to:
1. Not specify a hint (no comparison, backward compatible)
2. Explicitly specify counter_reset_hint:unknown (verify it's unknown)
3. Explicitly specify counter_reset_hint:gauge/reset/not_reset (verify match)
Fixes#17615
Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
Currently both the backend and frontend printers/formatters/serializers
incorrectly transform the following expression:
```
up * ignoring() group_left(__name__) node_boot_time_seconds
```
...into:
```
up * node_boot_time_seconds
```
...which yields a different result (including the metric name in the result
vs. no metric name).
We need to keep empty `ignoring()` modifiers if there is a grouping modifier
present.
Signed-off-by: Julius Volz <julius.volz@gmail.com>