Merge pull request #18684 from roidelapluie/roidelapluie/revert-duration-expr-stable

Revert "PromQL: Promote duration expressions as stable"
This commit is contained in:
Bartlomiej Plotka 2026-05-13 11:22:08 +02:00 committed by GitHub
commit e793b26713
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 197 additions and 111 deletions

View File

@ -260,7 +260,8 @@ func (c *flagConfig) setFeatureListOptions(logger *slog.Logger) error {
c.parserOpts.EnableExperimentalFunctions = true
logger.Info("Experimental PromQL functions enabled.")
case "promql-duration-expr":
logger.Warn("This option for --enable-feature is now permanently enabled and therefore a no-op.", "option", o)
c.parserOpts.ExperimentalDurationExpr = true
logger.Info("Experimental duration expression parsing enabled.")
case "native-histograms":
logger.Warn("This option for --enable-feature is a no-op. To scrape native histograms, set the scrape_native_histograms scrape config setting to true.", "option", o)
case "ooo-native-histograms":
@ -619,7 +620,7 @@ func main() {
a.Flag("scrape.discovery-reload-interval", "Interval used by scrape manager to throttle target groups updates.").
Hidden().Default("5s").SetValue(&cfg.scrape.DiscoveryReloadInterval)
a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, st-storage, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details.").
a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-duration-expr, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, st-storage, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details.").
StringsVar(&cfg.featureList)
a.Flag("agent", "Run Prometheus in 'Agent mode'.").BoolVar(&agentMode)

View File

@ -29,7 +29,7 @@
"bool": true,
"by": true,
"delayed_name_removal": false,
"duration_expr": true,
"duration_expr": false,
"fill": false,
"fill_left": false,
"fill_right": false,

View File

@ -320,7 +320,7 @@ func main() {
promQLLabelsDeleteQuery := promQLLabelsDeleteCmd.Arg("query", "PromQL query.").Required().String()
promQLLabelsDeleteName := promQLLabelsDeleteCmd.Arg("name", "Name of the label to delete.").Required().String()
featureList := app.Flag("enable-feature", "Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details").Default("").Strings()
featureList := app.Flag("enable-feature", "Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-duration-expr, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details").Default("").Strings()
documentationCmd := app.Command("write-documentation", "Generate command line documentation. Internal use.").Hidden()
@ -358,7 +358,7 @@ func main() {
case "promql-delayed-name-removal":
promqlEnableDelayedNameRemoval = true
case "promql-duration-expr":
fmt.Printf(" WARNING: promql-duration-expr is now permanently enabled and therefore a no-op")
promtoolParserOpts.ExperimentalDurationExpr = true
case "promql-extended-range-selectors":
promtoolParserOpts.EnableExtendedRangeSelectors = true
case "":

View File

@ -62,7 +62,7 @@ The Prometheus monitoring server
| <code class="text-nowrap">--query.timeout</code> | Maximum time a query may take before being aborted. Use with server mode only. | `2m` |
| <code class="text-nowrap">--query.max-concurrency</code> | Maximum number of queries executed concurrently. Use with server mode only. | `20` |
| <code class="text-nowrap">--query.max-samples</code> | Maximum number of samples a single query can load into memory. Note that queries will fail if they try to load more samples than this into memory, so this also limits the number of samples a query can return. Use with server mode only. | `50000000` |
| <code class="text-nowrap">--enable-feature</code> <code class="text-nowrap">...<code class="text-nowrap"> | Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, st-storage, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | |
| <code class="text-nowrap">--enable-feature</code> <code class="text-nowrap">...<code class="text-nowrap"> | Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-duration-expr, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, st-storage, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | |
| <code class="text-nowrap">--agent</code> | Run Prometheus in 'Agent mode'. | |
| <code class="text-nowrap">--log.level</code> | Only log messages with the given severity or above. One of: [debug, info, warn, error] | `info` |
| <code class="text-nowrap">--log.format</code> | Output format of log messages. One of: [logfmt, json] | `logfmt` |

View File

@ -12,7 +12,7 @@ Tooling for the Prometheus monitoring system.
| <code class="text-nowrap">-h</code>, <code class="text-nowrap">--help</code> | Show context-sensitive help (also try --help-long and --help-man). |
| <code class="text-nowrap">--version</code> | Show application version. |
| <code class="text-nowrap">--experimental</code> | Enable experimental commands. |
| <code class="text-nowrap">--enable-feature</code> <code class="text-nowrap">...<code class="text-nowrap"> | Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details |
| <code class="text-nowrap">--enable-feature</code> <code class="text-nowrap">...<code class="text-nowrap"> | Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-duration-expr, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details |

View File

@ -192,6 +192,63 @@ state is mutex guarded. Cumulative-only OTLP requests are not affected.
[d2c]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/deltatocumulativeprocessor
## PromQL arithmetic expressions in time durations
`--enable-feature=promql-duration-expr`
With this flag, arithmetic expressions can be used in time durations in range queries and offset durations.
In range queries:
```
rate(http_requests_total[5m * 2]) # 10 minute range
rate(http_requests_total[(5+2) * 1m]) # 7 minute range
```
In offset durations:
```
http_requests_total offset (1h / 2) # 30 minute offset
http_requests_total offset ((2 ^ 3) * 1m) # 8 minute offset
```
When using offset with duration expressions, you must wrap the expression in
parentheses. Without parentheses, only the first duration value will be used in
the offset calculation.
`step()` can be used in duration expressions.
For a **range query**, it resolves to the step width of the range query.
For an **instant query**, it resolves to `0s`.
`range()` can be used in duration expressions.
For a **range query**, it resolves to the full range of the query (end time - start time).
For an **instant query**, it resolves to `0s`.
This is particularly useful in combination with `@end()` to look back over the entire query range, e.g., `max_over_time(metric[range()] @ end())`.
`min(<duration>, <duration>)` and `max(<duration>, <duration>)` can be used to find the minimum or maximum of two duration expressions.
**Note**: Duration expressions are not supported in the @ timestamp operator.
The following operators are supported:
* `+` - addition
* `-` - subtraction
* `*` - multiplication
* `/` - division
* `%` - modulo
* `^` - exponentiation
Examples of equivalent durations:
* `5m * 2` is equivalent to `10m` or `600s`
* `10m - 1m` is equivalent to `9m` or `540s`
* `(5+2) * 1m` is equivalent to `7m` or `420s`
* `1h / 2` is equivalent to `30m` or `1800s`
* `4h % 3h` is equivalent to `1h` or `3600s`
* `(2 ^ 3) * 1m` is equivalent to `8m` or `480s`
* `step() + 1` is equivalent to the query step width increased by 1s.
* `max(step(), 5s)` is equivalent to the larger of the query step width and `5s`.
* `min(2 * step() + 5s, 5m)` is equivalent to the smaller of twice the query step increased by `5s` and `5m`.
## OTLP Native Delta Support
`--enable-feature=otlp-native-delta-ingestion`

View File

@ -174,57 +174,6 @@ Examples:
12h34m56s # Equivalent to 45296s and thus 45296.
54s321ms # Equivalent to 54.321.
#### Duration expressions
Duration expressions can be used in range selectors, subquery range and
resolution fields, and offset durations.
Examples:
rate(http_requests_total[5m * 2]) # 10 minute range.
rate(http_requests_total[(5 + 2) * 1m]) # 7 minute range.
http_requests_total offset (1h / 2) # 30 minute offset.
http_requests_total offset ((2 ^ 3) * 1m) # 8 minute offset.
When using offset with duration expressions, you must wrap the expression in
parentheses. Without parentheses, only the first duration value will be used in
the offset calculation.
`step()` can be used in duration expressions. For a range query, it resolves to
the step width of the range query. For an instant query, it resolves to `0s`.
`range()` can be used in duration expressions. For a range query, it resolves to
the full range of the query (end time minus start time). For an instant query,
it resolves to `0s`. This is particularly useful in combination with `@ end()`
to look back over the entire query range, e.g.,
`max_over_time(metric[range()] @ end())`.
`min(<duration>, <duration>)` and `max(<duration>, <duration>)` can be used to
find the minimum or maximum of two duration expressions.
Duration expressions are not supported in the @ timestamp operator.
The following operators are supported:
* `+` - addition.
* `-` - subtraction.
* `*` - multiplication.
* `/` - division.
* `%` - modulo.
* `^` - exponentiation.
Examples of equivalent durations:
* `5m * 2` is equivalent to `10m` or `600s`.
* `10m - 1m` is equivalent to `9m` or `540s`.
* `(5 + 2) * 1m` is equivalent to `7m` or `420s`.
* `1h / 2` is equivalent to `30m` or `1800s`.
* `4h % 3h` is equivalent to `1h` or `3600s`.
* `(2 ^ 3) * 1m` is equivalent to `8m` or `480s`.
* `step() + 1` is equivalent to the query step width increased by 1s.
* `max(step(), 5s)` is equivalent to the larger of the query step width and `5s`.
* `min(2 * step() + 5s, 5m)` is equivalent to the smaller of twice the query step increased by `5s` and `5m`.
## Time series selectors
These are the basic building-blocks that instruct PromQL what data to fetch.
@ -333,13 +282,14 @@ A workaround for this restriction is to use the `__name__` label:
Range vector literals work like instant vector literals, except that they
select a range of samples back from the current instant. Syntactically, a
[duration](#float-literals-and-time-durations) is appended in square brackets
(`[]`) at the end of a vector selector to specify how far back in time values
should be fetched for each resulting range vector element. Commonly, this uses
one or more time units, e.g. `[5m]`. The range is a left-open and right-closed
interval, i.e. samples with timestamps coinciding with the left boundary of the
range are excluded from the selection, while samples coinciding with the right
boundary of the range are included in the selection.
[float literal](#float-literals-and-time-durations) is appended in square
brackets (`[]`) at the end of a vector selector to specify for how many seconds
back in time values should be fetched for each resulting range vector element.
Commonly, the float literal uses the syntax with one or more time units, e.g.
`[5m]`. The range is a left-open and right-closed interval, i.e. samples with
timestamps coinciding with the left boundary of the range are excluded from the
selection, while samples coinciding with the right boundary of the range are
included in the selection.
In this example, we select all the values recorded less than 5m ago for all
time series that have the metric name `http_requests_total` and a `job` label
@ -429,9 +379,8 @@ Note that the `@` modifier allows a query to look ahead of its evaluation time.
Subquery allows you to run an instant query for a given range and resolution. The result of a subquery is a range vector.
Syntax: `<instant_query> '[' <range> ':' [<resolution>] ']' [ @ <float_literal> ] [ offset <duration> ]`
Syntax: `<instant_query> '[' <range> ':' [<resolution>] ']' [ @ <float_literal> ] [ offset <float_literal> ]`
* `<range>`, `<resolution>`, and `offset` support duration expressions.
* `<resolution>` is optional. Default is the global evaluation interval.
## Operators

View File

@ -961,7 +961,8 @@ These functions act on histograms in the following way:
select the first sample of `m` _within_ the 1m range, where `m offset 1m` will
select the most recent sample within the lookback interval _outside and prior
to_ the 1m offset. This is particularly useful with `first_over_time(m[step()])`
in range queries to ensure that the sample selected is within the range step.
in range queries (available when `--enable-feature=promql-duration-expr` is set)
to ensure that the sample selected is within the range step.
## Trigonometric Functions

View File

@ -23,7 +23,7 @@ import (
)
func TestDurationVisitor(t *testing.T) {
p := parser.NewParser(parser.Options{})
p := parser.NewParser(parser.Options{ExperimentalDurationExpr: true})
complexExpr := `sum_over_time(
rate(metric[5m] offset 1h)[10m:30s] offset 2h
) +

View File

@ -53,6 +53,6 @@ func (pql *promQLParser) RegisterFeatures(r features.Collector) {
r.Set(features.PromQLFunctions, f, !fc.Experimental || pql.options.EnableExperimentalFunctions)
}
// Register parser features.
r.Enable(features.PromQL, "duration_expr")
// Register experimental parser features.
r.Set(features.PromQL, "duration_expr", pql.options.ExperimentalDurationExpr)
}

View File

@ -1201,23 +1201,27 @@ offset_duration_expr : number_duration_literal
}
| STEP LEFT_PAREN RIGHT_PAREN
{
$$ = &DurationExpr{
Op: STEP,
de := &DurationExpr{
Op: STEP,
StartPos: $1.PositionRange().Start,
EndPos: $3.PositionRange().End,
EndPos: $3.PositionRange().End,
}
yylex.(*parser).experimentalDurationExpr(de)
$$ = de
}
| RANGE LEFT_PAREN RIGHT_PAREN
{
$$ = &DurationExpr{
Op: RANGE,
de := &DurationExpr{
Op: RANGE,
StartPos: $1.PositionRange().Start,
EndPos: $3.PositionRange().End,
EndPos: $3.PositionRange().End,
}
yylex.(*parser).experimentalDurationExpr(de)
$$ = de
}
| unary_op STEP LEFT_PAREN RIGHT_PAREN
{
$$ = &DurationExpr{
de := &DurationExpr{
Op: $1.Typ,
RHS: &DurationExpr{
Op: STEP,
@ -1226,10 +1230,12 @@ offset_duration_expr : number_duration_literal
},
StartPos: $1.Pos,
}
yylex.(*parser).experimentalDurationExpr(de)
$$ = de
}
| unary_op RANGE LEFT_PAREN RIGHT_PAREN
{
$$ = &DurationExpr{
de := &DurationExpr{
Op: $1.Typ,
RHS: &DurationExpr{
Op: RANGE,
@ -1238,20 +1244,24 @@ offset_duration_expr : number_duration_literal
},
StartPos: $1.Pos,
}
yylex.(*parser).experimentalDurationExpr(de)
$$ = de
}
| min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
{
$$ = &DurationExpr{
de := &DurationExpr{
Op: $1.Typ,
StartPos: $1.PositionRange().Start,
EndPos: $6.PositionRange().End,
LHS: $3.(Expr),
RHS: $5.(Expr),
}
yylex.(*parser).experimentalDurationExpr(de)
$$ = de
}
| unary_op min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
{
$$ = &DurationExpr{
de := &DurationExpr{
Op: $1.Typ,
StartPos: $1.Pos,
EndPos: $6.PositionRange().End,
@ -1263,6 +1273,8 @@ offset_duration_expr : number_duration_literal
RHS: $6.(Expr),
},
}
yylex.(*parser).experimentalDurationExpr(de)
$$ = de
}
| unary_op LEFT_PAREN duration_expr RIGHT_PAREN %prec MUL
{
@ -1327,18 +1339,22 @@ duration_expr : number_duration_literal
}
| duration_expr ADD duration_expr
{
yylex.(*parser).experimentalDurationExpr($1.(Expr))
$$ = &DurationExpr{Op: ADD, LHS: $1.(Expr), RHS: $3.(Expr)}
}
| duration_expr SUB duration_expr
{
yylex.(*parser).experimentalDurationExpr($1.(Expr))
$$ = &DurationExpr{Op: SUB, LHS: $1.(Expr), RHS: $3.(Expr)}
}
| duration_expr MUL duration_expr
{
yylex.(*parser).experimentalDurationExpr($1.(Expr))
$$ = &DurationExpr{Op: MUL, LHS: $1.(Expr), RHS: $3.(Expr)}
}
| duration_expr DIV duration_expr
{
yylex.(*parser).experimentalDurationExpr($1.(Expr))
if nl, ok := $3.(*NumberLiteral); ok && nl.Val == 0 {
yylex.(*parser).addParseErrf($2.PositionRange(), "division by zero")
$$ = &NumberLiteral{Val: 0}
@ -1348,6 +1364,7 @@ duration_expr : number_duration_literal
}
| duration_expr MOD duration_expr
{
yylex.(*parser).experimentalDurationExpr($1.(Expr))
if nl, ok := $3.(*NumberLiteral); ok && nl.Val == 0 {
yylex.(*parser).addParseErrf($2.PositionRange(), "modulo by zero")
$$ = &NumberLiteral{Val: 0}
@ -1357,39 +1374,47 @@ duration_expr : number_duration_literal
}
| duration_expr POW duration_expr
{
yylex.(*parser).experimentalDurationExpr($1.(Expr))
$$ = &DurationExpr{Op: POW, LHS: $1.(Expr), RHS: $3.(Expr)}
}
| STEP LEFT_PAREN RIGHT_PAREN
{
$$ = &DurationExpr{
de := &DurationExpr{
Op: STEP,
StartPos: $1.PositionRange().Start,
EndPos: $3.PositionRange().End,
}
yylex.(*parser).experimentalDurationExpr(de)
$$ = de
}
| RANGE LEFT_PAREN RIGHT_PAREN
{
$$ = &DurationExpr{
de := &DurationExpr{
Op: RANGE,
StartPos: $1.PositionRange().Start,
EndPos: $3.PositionRange().End,
}
yylex.(*parser).experimentalDurationExpr(de)
$$ = de
}
| min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
{
$$ = &DurationExpr{
de := &DurationExpr{
Op: $1.Typ,
StartPos: $1.PositionRange().Start,
EndPos: $6.PositionRange().End,
LHS: $3.(Expr),
RHS: $5.(Expr),
}
yylex.(*parser).experimentalDurationExpr(de)
$$ = de
}
| paren_duration_expr
;
paren_duration_expr : LEFT_PAREN duration_expr RIGHT_PAREN
{
yylex.(*parser).experimentalDurationExpr($2.(Expr))
if durationExpr, ok := $2.(*DurationExpr); ok {
durationExpr.Wrapped = true
$$ = durationExpr

View File

@ -2316,25 +2316,29 @@ yydefault:
case 282:
yyDollar = yyS[yypt-3 : yypt+1]
{
yyVAL.node = &DurationExpr{
de := &DurationExpr{
Op: STEP,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[3].item.PositionRange().End,
}
yylex.(*parser).experimentalDurationExpr(de)
yyVAL.node = de
}
case 283:
yyDollar = yyS[yypt-3 : yypt+1]
{
yyVAL.node = &DurationExpr{
de := &DurationExpr{
Op: RANGE,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[3].item.PositionRange().End,
}
yylex.(*parser).experimentalDurationExpr(de)
yyVAL.node = de
}
case 284:
yyDollar = yyS[yypt-4 : yypt+1]
{
yyVAL.node = &DurationExpr{
de := &DurationExpr{
Op: yyDollar[1].item.Typ,
RHS: &DurationExpr{
Op: STEP,
@ -2343,11 +2347,13 @@ yydefault:
},
StartPos: yyDollar[1].item.Pos,
}
yylex.(*parser).experimentalDurationExpr(de)
yyVAL.node = de
}
case 285:
yyDollar = yyS[yypt-4 : yypt+1]
{
yyVAL.node = &DurationExpr{
de := &DurationExpr{
Op: yyDollar[1].item.Typ,
RHS: &DurationExpr{
Op: RANGE,
@ -2356,22 +2362,26 @@ yydefault:
},
StartPos: yyDollar[1].item.Pos,
}
yylex.(*parser).experimentalDurationExpr(de)
yyVAL.node = de
}
case 286:
yyDollar = yyS[yypt-6 : yypt+1]
{
yyVAL.node = &DurationExpr{
de := &DurationExpr{
Op: yyDollar[1].item.Typ,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[6].item.PositionRange().End,
LHS: yyDollar[3].node.(Expr),
RHS: yyDollar[5].node.(Expr),
}
yylex.(*parser).experimentalDurationExpr(de)
yyVAL.node = de
}
case 287:
yyDollar = yyS[yypt-7 : yypt+1]
{
yyVAL.node = &DurationExpr{
de := &DurationExpr{
Op: yyDollar[1].item.Typ,
StartPos: yyDollar[1].item.Pos,
EndPos: yyDollar[6].node.PositionRange().End,
@ -2383,6 +2393,8 @@ yydefault:
RHS: yyDollar[6].node.(Expr),
},
}
yylex.(*parser).experimentalDurationExpr(de)
yyVAL.node = de
}
case 288:
yyDollar = yyS[yypt-4 : yypt+1]
@ -2446,21 +2458,25 @@ yydefault:
case 294:
yyDollar = yyS[yypt-3 : yypt+1]
{
yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr))
yyVAL.node = &DurationExpr{Op: ADD, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)}
}
case 295:
yyDollar = yyS[yypt-3 : yypt+1]
{
yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr))
yyVAL.node = &DurationExpr{Op: SUB, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)}
}
case 296:
yyDollar = yyS[yypt-3 : yypt+1]
{
yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr))
yyVAL.node = &DurationExpr{Op: MUL, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)}
}
case 297:
yyDollar = yyS[yypt-3 : yypt+1]
{
yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr))
if nl, ok := yyDollar[3].node.(*NumberLiteral); ok && nl.Val == 0 {
yylex.(*parser).addParseErrf(yyDollar[2].item.PositionRange(), "division by zero")
yyVAL.node = &NumberLiteral{Val: 0}
@ -2471,6 +2487,7 @@ yydefault:
case 298:
yyDollar = yyS[yypt-3 : yypt+1]
{
yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr))
if nl, ok := yyDollar[3].node.(*NumberLiteral); ok && nl.Val == 0 {
yylex.(*parser).addParseErrf(yyDollar[2].item.PositionRange(), "modulo by zero")
yyVAL.node = &NumberLiteral{Val: 0}
@ -2481,40 +2498,48 @@ yydefault:
case 299:
yyDollar = yyS[yypt-3 : yypt+1]
{
yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr))
yyVAL.node = &DurationExpr{Op: POW, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)}
}
case 300:
yyDollar = yyS[yypt-3 : yypt+1]
{
yyVAL.node = &DurationExpr{
de := &DurationExpr{
Op: STEP,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[3].item.PositionRange().End,
}
yylex.(*parser).experimentalDurationExpr(de)
yyVAL.node = de
}
case 301:
yyDollar = yyS[yypt-3 : yypt+1]
{
yyVAL.node = &DurationExpr{
de := &DurationExpr{
Op: RANGE,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[3].item.PositionRange().End,
}
yylex.(*parser).experimentalDurationExpr(de)
yyVAL.node = de
}
case 302:
yyDollar = yyS[yypt-6 : yypt+1]
{
yyVAL.node = &DurationExpr{
de := &DurationExpr{
Op: yyDollar[1].item.Typ,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[6].item.PositionRange().End,
LHS: yyDollar[3].node.(Expr),
RHS: yyDollar[5].node.(Expr),
}
yylex.(*parser).experimentalDurationExpr(de)
yyVAL.node = de
}
case 304:
yyDollar = yyS[yypt-3 : yypt+1]
{
yylex.(*parser).experimentalDurationExpr(yyDollar[2].node.(Expr))
if durationExpr, ok := yyDollar[2].node.(*DurationExpr); ok {
durationExpr.Wrapped = true
yyVAL.node = durationExpr

View File

@ -43,6 +43,7 @@ var parserPool = sync.Pool{
// Options holds the configuration for the PromQL parser.
type Options struct {
EnableExperimentalFunctions bool
ExperimentalDurationExpr bool
EnableExtendedRangeSelectors bool
EnableBinopFillModifiers bool
}
@ -1192,6 +1193,12 @@ func (p *parser) getAtModifierVars(e Node) (**int64, *ItemType, *posrange.Pos, b
return timestampp, preprocp, endPosp, true
}
func (p *parser) experimentalDurationExpr(e Expr) {
if !p.options.ExperimentalDurationExpr {
p.addParseErrf(e.PositionRange(), "experimental duration expression is not enabled")
}
}
func MustLabelMatcher(mt labels.MatchType, name, val string) *labels.Matcher {
m, err := labels.NewMatcher(mt, name, val)
if err != nil {

View File

@ -5327,6 +5327,7 @@ func readable(s string) string {
func TestParseExpressions(t *testing.T) {
optsParser := NewParser(Options{
EnableExperimentalFunctions: true,
ExperimentalDurationExpr: true,
})
for _, test := range testExpr {
@ -6083,6 +6084,7 @@ func TestParseCustomFunctions(t *testing.T) {
func TestNewParser(t *testing.T) {
p := NewParser(Options{
EnableExperimentalFunctions: true,
ExperimentalDurationExpr: true,
})
// ParseExpr should work.

View File

@ -670,7 +670,7 @@ func TestUnaryPretty(t *testing.T) {
}
func TestDurationExprPretty(t *testing.T) {
optsParser := NewParser(Options{})
optsParser := NewParser(Options{ExperimentalDurationExpr: true})
maxCharactersPerLine = 10
inputs := []struct {
in, out string

View File

@ -23,6 +23,7 @@ import (
func TestExprString(t *testing.T) {
optsParser := NewParser(Options{
ExperimentalDurationExpr: true,
EnableExtendedRangeSelectors: true,
EnableBinopFillModifiers: true,
})

View File

@ -90,6 +90,7 @@ func LoadedStorage(t testing.TB, input string) *teststorage.TestStorage {
// TestParserOpts are the parser options used for all built-in test engines.
var TestParserOpts = parser.Options{
EnableExperimentalFunctions: true,
ExperimentalDurationExpr: true,
EnableExtendedRangeSelectors: true,
EnableBinopFillModifiers: true,
}

View File

@ -363,6 +363,7 @@ func FuzzParseExpr(f *testing.F) {
p := parser.NewParser(parser.Options{
EnableExperimentalFunctions: true,
ExperimentalDurationExpr: true,
EnableExtendedRangeSelectors: true,
EnableBinopFillModifiers: true,
})

View File

@ -22,7 +22,7 @@ import (
)
func TestTranslateASTDurationExpressions(t *testing.T) {
p := parser.NewParser(parser.Options{})
p := parser.NewParser(parser.Options{ExperimentalDurationExpr: true})
type tc struct {
name string

View File

@ -589,7 +589,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -908,7 +909,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -1238,7 +1240,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -2069,7 +2072,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -2219,7 +2223,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -2328,7 +2333,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -2437,7 +2443,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -2652,7 +2659,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -2761,7 +2769,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -3274,7 +3283,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -3383,7 +3393,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -3508,7 +3519,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -3773,7 +3785,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -3882,7 +3895,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -3991,7 +4005,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),
@ -4100,7 +4115,8 @@ const funcDocs: Record<string, React.ReactNode> = {
first sample of <code>m</code> <em>within</em> the 1m range, where <code>m offset 1m</code> will select the most
recent sample within the lookback interval <em>outside and prior to</em> the 1m offset. This is particularly
useful with <code>first_over_time(m[step()])</code>
in range queries to ensure that the sample selected is within the range step.
in range queries (available when <code>--enable-feature=promql-duration-expr</code> is set) to ensure that the
sample selected is within the range step.
</p>
</>
),