fix(promql): keep fill_left and fill_right on their written sides under group_right (#9899)

I hereby agree to the terms of the [GreptimeDB
CLA](https://github.com/GreptimeTeam/.github/blob/main/CLA.md).

## Refer to a related PR or issue link (optional)

prometheus/prometheus#19939

## What's changed and what's your intention?

The native binary kernel applied `fill_left` / `fill_right` after
swapping the operands of `group_right`, so under `group_right`
`fill_left` filled a missing right-hand sample and `fill_right` a
missing left-hand one. That is the Prometheus behavior before
prometheus/prometheus#19939, which was merged into Prometheus main on
2026-10-13: the fill values now swap together with the operands, so they
always refer to the side written in the query.

For example, `node_meta - on(instance) group_right fill_left(5)
fill_right(7) cpu_info`: a missing `node_meta` must give `5 - cpu_info`
and a missing `cpu_info` must give `node_meta - 7`.

- `binary_field` swaps `fill.lhs` / `fill.rhs` when the one side is the
left, matching `VectorBinop` after #19939. The `FillValues` doc now
states the as-written meaning.
- `fill_cases.txt` is regenerated by promql-compat
`narthex-golden-vector -experimental fill` built against Prometheus at
the #19939 merge commit (same seed and case count). Only the 24
`group_right` cases whose fill fires on an asymmetric pair change.

No released Prometheus contains #19939 yet (v3.15.1 still has the old
behavior), so until the next release native results differ from released
Prometheus for asymmetric fills under `group_right`. The compiler does
not bind vector-vector fill modifiers yet, so this is not reachable from
a query today; it has to land before that wiring does.

## PR Checklist
Please convert it to a draft if some of the following conditions are not
met.

- [x] I have written the necessary rustdoc comments.
- [x] I have added the necessary unit tests and integration tests.
- [ ] This PR requires documentation updates.
- [x] API changes are backward compatible.
- [x] Schema or data changes are backward compatible.
- [ ] This PR needs to be backported to release branches, and I have
added the `backport-<target>` labels (e.g. `backport-v1.3` targets
`release/v1.3`).
This commit is contained in:
dennis zhuang
2026-10-23 16:44:07 +08:00
committed by GitHub
3 changed files with 68 additions and 44 deletions
+12 -5
View File
@@ -144,10 +144,8 @@ pub struct BinaryMatching {
}
/// The values of `fill`, `fill_left` and `fill_right`, which stand in for a sample that has no
/// match on the other side.
///
/// Prometheus applies them after it swaps the operands of `group_right`, so there `fill_left`
/// stands in for a missing right-hand sample and `fill_right` for a missing left-hand one.
/// match on the other side: `lhs` for a missing left-hand sample and `rhs` for a missing
/// right-hand one, as the query writes them, `group_right` included (prometheus/prometheus#19939).
#[derive(Clone, Copy, Debug, Default)]
pub struct FillValues {
pub lhs: Option<f64>,
@@ -736,7 +734,16 @@ fn binary_field(
}
None => FieldMetadata::default(),
};
let fill = matching.fill;
// The walk below fills `rhs` for a missing one-side sample and `lhs` for a missing many-side
// one, so under `group_right`, whose one side is the left, the two trade places.
let fill = if one_is_left {
FillValues {
lhs: matching.fill.rhs,
rhs: matching.fill.lhs,
}
} else {
matching.fill
};
// Prometheus short-circuits a point where either side is empty, before any check; with a fill
// value only a point where both are empty.
let filling = fill.lhs.is_some() || fill.rhs.is_some();
+54 -37
View File
@@ -1,4 +1,4 @@
# Generated by promql-compat cmd/narthex-golden-vector built for amd64 (Prometheus v3.15.0
# Generated by promql-compat cmd/narthex-golden-vector built for amd64 (Prometheus v0.314.0-rc.0.0.20261013115751-351c7f6548b2
# engine, seed 1, 200 cases per kind, -experimental fill). Each case lists its operation, the grid, the
# input series and either the result series or the error. Points are `grid index:hex bits`.
case binary - false ignoring zone one - 1 1
@@ -197,7 +197,7 @@ grid 100000 10000 1
series __name__=a,host=h2,job=y,zone=z1 ; 0:4086be0000000000
series __name__=b,host=h2,job=y,zone=z1 ; 0:c0898b0000000000
series __name__=b,host=h1,job=y ; 0:c072680000000000
result host=h1 ; 0:7ff8000000000001
result host=h1 ; 0:3f8162a981f4b312
result host=h2,job=y,zone=z1 ; 0:bfec7dbfb7568f5e
end
case binary ^ false on job one - - 1
@@ -247,9 +247,9 @@ series __name__=a,host=h2,job=x,zone=z1 ; 1:406cf80000000000 2:4071a40000000000
series __name__=a,host=h1,job=y ; 0:408eb30000000000 1:c085ad0000000000 2:4066e80000000000 3:407ea60000000000 5:c080320000000000
series __name__=a,host=h2,job=y,zone=z1 ;
series __name__=b,host=h2,job=x ; 6:c07a260000000000
result host=h1 ; 0:408eb30000000000 1:c085ad0000000000 2:4066e80000000000 3:407ea60000000000 5:c080320000000000
result host=h2,job=x ; 6:7ff0000000000000
result host=h2,zone=z1 ; 1:406cf80000000000 2:4071a40000000000 5:c08a6e0000000000 6:408a5d0000000000
result host=h1 ; 0:7ff0000000000000 1:7ff0000000000000 2:7ff0000000000000 3:7ff0000000000000 5:7ff0000000000000
result host=h2,job=x ; 6:c07a260000000000
result host=h2,zone=z1 ; 1:7ff0000000000000 2:7ff0000000000000 5:7ff0000000000000 6:7ff0000000000000
end
case binary / false ignoring job left zone - 1
grid 100000 10000 7
@@ -312,13 +312,13 @@ series __name__=b,host=h2,job=y,zone=z1 ; 2:c07a780000000000 3:406a400000000000
series __name__=b,host=h1,job=x ; 5:7ff8000000000001 6:4084bf0000000000
series __name__=b,host=h1,job=x,zone=z1 ; 5:c06f5c0000000000 7:7ff8000000000001
series __name__=b,host=h2,job=y ; 1:c0746e0000000000 2:40870f0000000000 3:4083c60000000000
result host=h1,job=x ; 7:fff0000000000000
result host=h1,job=x,zone=z1 ; 3:7ff8000000000001 4:7ff0000000000000 5:c103348fe0000000 6:7ff0000000000000
result host=h2,job=y ; 4:7ff0000000000000 5:7ff0000000000000 7:fff0000000000000
result job=x ; 5:7ff8000000000001 6:c099eec000000000
result host=h1,job=x ; 7:4081530000000000
result host=h1,job=x,zone=z1 ; 3:7ff8000000000001 4:c0858d8000000000 5:c103348fe0000000 6:c0a3164000000000
result host=h2,job=y ; 4:c083600000000000 5:c0985d8000000000 7:4081558000000000
result job=x ; 5:7ff8000000000001 6:7ff0000000000000
result job=x,zone=z1 ; 7:7ff8000000000001
result job=y ; 1:4089898000000000 2:c09cd2c000000000 3:c098b78000000000
result job=y,zone=z1 ; 2:40908b0000000000 3:c080680000000000 4:c06bc60000000000
result job=y ; 1:fff0000000000000 2:7ff0000000000000 3:7ff0000000000000
result job=y,zone=z1 ; 2:fff0000000000000 3:7ff0000000000000 4:7ff0000000000000
end
case binary % false ignoring host,job,zone right host,zone NaN NaN
grid 100000 10000 12
@@ -336,7 +336,6 @@ series __name__=a,host=h1,job=x ; 0:c067700000000000
series __name__=b,host=h1,job=y,zone=z1 ;
series __name__=b,host=h1,job=x ; 0:4065d80000000000
result host=h1,job=x ; 0:c029800000000000
result host=h2,job=y ; 0:7ff0000000000000
end
case binary > true on host,job,zone right - 0 -
grid 100000 10000 11
@@ -347,14 +346,16 @@ series __name__=b,host=h1,job=x,zone=z1 ; 7:4085780000000000 8:c085210000000000
series __name__=b,host=h1,job=x ; 5:c0789e0000000000 7:7ff8000000000001
series __name__=b,host=h2,job=y ; 8:404d400000000000
series __name__=b,host=h1,job=x,zone=z2 ; 10:40864f0000000000
result host=h1,job=x ; 3:0
result host=h1,job=x,zone=z1 ; 2:0 3:3ff0000000000000 4:0 5:3ff0000000000000 6:0 7:0 8:0
result host=h1,job=y ; 2:3ff0000000000000 3:0 4:0 6:0 7:3ff0000000000000 8:0
result host=h1,job=x ; 5:3ff0000000000000 7:0
result host=h1,job=x,zone=z1 ; 7:0 8:0 9:3ff0000000000000
result host=h1,job=x,zone=z2 ; 10:0
result host=h2,job=y ; 8:0
end
case binary < false ignoring job,zone right - - 1
grid 100000 10000 4
series __name__=a,host=h1,job=y,zone=z1 ; 0:4087720000000000 1:4082dd0000000000 2:0 3:c07b700000000000
series __name__=b,host=h1,job=y,zone=z1 ; 0:c0705a0000000000 1:c0866e0000000000
result host=h1 ; 2:0 3:c07b700000000000
end
case binary + false none - one - NaN -
grid 100000 10000 12
@@ -441,6 +442,9 @@ series __name__=a,host=h2,job=x,zone=z1 ; 0:c0878a0000000000
series __name__=a,host=h1,job=y ; 0:c0838a0000000000
series __name__=b,host=h1,job=y ;
series __name__=b,host=h1,job=y,zone=z2 ;
result job=x ; 0:7ff0000000000000
result job=x,zone=z1 ; 0:7ff0000000000000
result job=y ; 0:7ff0000000000000
end
case binary == true ignoring zone left job Inf Inf
grid 100000 10000 5
@@ -480,7 +484,9 @@ series __name__=a,host=h1,job=y ; 8:7e37e43c8800759c
series __name__=a,host=h2,job=y ; 6:c05e480000000000 7:c084800000000000
series __name__=a,host=h2,job=y,zone=z1 ; 1:c08c670000000000 2:407bd00000000000 3:0 4:4078840000000000 5:40781a0000000000 6:40849c0000000000 8:408ea10000000000 9:40743e0000000000 10:4075fa0000000000
series __name__=b,host=h1,job=y ; 7:c08a420000000000 9:405f180000000000
result host=h1,job=y ; 7:0 9:3ff0000000000000
result host=h1 ; 8:0
result host=h2 ; 6:3ff0000000000000 7:3ff0000000000000
result host=h2,zone=z1 ; 1:3ff0000000000000 2:0 3:0 4:0 5:0 6:0 8:0 9:0 10:0
end
case binary % false on job left zone NaN NaN
grid 100000 10000 1
@@ -542,8 +548,9 @@ series __name__=a,host=h2,job=y ; 5:4086fd0000000000 6:408ced0000000000 8:c07d6e
series __name__=a,host=h1,job=x ; 4:407b060000000000 5:c068b40000000000 6:c07bee0000000000
series __name__=b,host=h2,job=y ; 4:4059c00000000000 5:4083760000000000
series __name__=b,host=h1,job=x ; 2:c084730000000000 3:408ac60000000000 4:4042d00000000000 5:c08daf0000000000 6:40736c0000000000 7:40785a0000000000
result __name__=b,host=h1,job=x ; 6:c07bee0000000000
result host=h2,job=y ; 8:c07d6e0000000000
result __name__=b,host=h1,job=x ; 3:0 6:c07bee0000000000 7:0
result __name__=b,host=h2,job=y ; 4:0
result host=h2,job=y ; 6:408ced0000000000 8:c07d6e0000000000
end
case binary / false none - one - - Inf
grid 100000 10000 4
@@ -701,8 +708,6 @@ grid 100000 10000 8
series __name__=a,host=h2,job=y ;
series __name__=b,host=h2,job=x ; 1:40800c0000000000 2:40756e0000000000
series __name__=b,host=h2,job=y ; 2:407a100000000000 5:c08efe0000000000 6:4051a00000000000 7:40750e0000000000
result host=h2,job=x ; 1:0 2:0
result host=h2,job=y ; 2:0 5:0 6:0 7:0
end
case binary * false ignoring job one - Inf Inf
grid 100000 10000 10
@@ -746,7 +751,7 @@ series __name__=a,host=h2,job=x ;
series __name__=b,host=h1,job=x ; 5:c077520000000000
series __name__=b,host=h2,job=x,zone=z1 ; 3:40746c0000000000 4:0 5:4076260000000000
result host=h1,job=x ; 5:bff5479c8412de20
result host=h2,job=x,zone=z1 ; 3:0 4:fff8000000000000 5:0
result host=h2,job=y ; 2:fff0000000000000 3:7ff0000000000000 4:fff0000000000000 5:fff0000000000000
end
case binary == false none - one - - 0
grid 100000 10000 4
@@ -816,6 +821,8 @@ series __name__=b,host=h1,job=x ; 4:405fd00000000000
series __name__=b,host=h2,job=x,zone=z1 ; 2:c088f70000000000 3:c08ed80000000000
result __name__=b,host=h1,job=x,zone=z1 ; 2:c083350000000000
result __name__=b,host=h2,job=x,zone=z1 ; 3:c065f80000000000
result host=h2 ; 2:4072720000000000
result host=h2,zone=z1 ; 5:408f3d0000000000
end
case binary % false ignoring - left host,job,zone - 1
grid 100000 10000 5
@@ -885,7 +892,8 @@ grid 100000 10000 8
series __name__=a,host=h1,job=x ; 4:c07afe0000000000 5:4083ab0000000000
series __name__=b,host=h1,job=x ; 2:c089f00000000000
series __name__=b,host=h2,job=y ; 0:7ff0000000000000 1:4085c60000000000
result host=h1 ; 4:0 5:0
result host=h1,job=x ; 2:0
result host=h2,job=y ; 0:0 1:0
end
case binary >= false on host,job left zone - 1
grid 100000 10000 3
@@ -998,6 +1006,8 @@ series __name__=a,host=h2,job=x ; 5:40844e0000000000
series __name__=a,host=h1,job=y,zone=z1 ; 3:c072a40000000000 4:4064a00000000000 5:40797e0000000000 6:c086d30000000000
series __name__=b,host=h1,job=y,zone=z1 ; 5:c0805a0000000000
series __name__=b,host=h2,job=x,zone=z1 ; 5:c076a80000000000 6:4088a20000000000 7:40833b0000000000
result job=x ; 5:40844e0000000000
result job=y,zone=z1 ; 3:c072a40000000000 4:4064a00000000000 6:c086d30000000000
end
case binary > false on - one - - NaN
grid 100000 10000 9
@@ -1151,7 +1161,6 @@ case binary <= true ignoring zone,__name__ right host,zone - 0
grid 100000 10000 10
series __name__=a,host=h2,job=y,zone=z1 ;
series __name__=b,host=h2,job=y,zone=z1 ; 9:c06bd40000000000
result host=h2,job=y ; 9:0
end
case binary > false on zone one - 0 -
grid 100000 10000 2
@@ -1235,8 +1244,6 @@ grid 100000 10000 6
series __name__=a,host=h2,job=x ;
series __name__=b,host=h2,job=x,zone=z1 ; 3:403ae00000000000 4:c08a2b0000000000
series __name__=b,host=h2,job=y ; 5:c086880000000000
result host=h2,job=x,zone=z1 ; 3:3ff0000000000000 4:3ff0000000000000
result host=h2,job=y ; 5:3ff0000000000000
end
case binary < false none - one - Inf Inf
grid 100000 10000 6
@@ -1327,7 +1334,9 @@ series __name__=a,host=h2,job=x ; 2:407a6a0000000000
series __name__=a,host=h2,job=y ; 0:0 1:4080950000000000 2:4089ce0000000000 3:c079060000000000
series __name__=a,host=h2,job=x,zone=z1 ; 5:4070680000000000
series __name__=b,host=h2,job=y ; 1:4058480000000000 2:4080f50000000000
result host=h2,job=y ; 1:76e18773d32422b0 2:7ff0000000000000
result host=h2,job=x ; 2:3ff0000000000000
result host=h2,job=x,zone=z1 ; 5:3ff0000000000000
result host=h2,job=y ; 0:3ff0000000000000 1:76e18773d32422b0 2:7ff0000000000000 3:3ff0000000000000
end
case binary - false ignoring - right job,zone NaN -
grid 100000 10000 5
@@ -1339,9 +1348,10 @@ series __name__=b,host=h2,job=x ; 3:4049600000000000
series __name__=b,host=h2,job=x,zone=z2 ; 1:c089410000000000
series __name__=b,host=h1,job=y,zone=z2 ; 1:4063e00000000000
series __name__=b,host=h1,job=y ; 2:c0801b0000000000 3:c086a20000000000 4:4074960000000000
result host=h1,job=y ; 4:407fac0000000000
result host=h2,job=x ; 2:7ff8000000000001 3:c0808b0000000000
result host=h2,job=y ; 4:7ff8000000000001
result host=h1,job=y ; 2:7ff8000000000001 3:7ff8000000000001 4:407fac0000000000
result host=h1,job=y,zone=z2 ; 1:7ff8000000000001
result host=h2,job=x ; 3:c0808b0000000000
result host=h2,job=x,zone=z2 ; 1:7ff8000000000001
end
case binary <= false ignoring host one - - 1
grid 100000 10000 2
@@ -1362,8 +1372,8 @@ series __name__=a,host=h1,job=y ;
series __name__=a,host=h2,job=x ; 0:4046e00000000000 1:c081960000000000 2:40812b0000000000 3:c08f1c0000000000 4:4079d40000000000
series __name__=b,host=h2,job=x,zone=z1 ; 3:4089100000000000
series __name__=b,host=h1,job=x ; 1:c085590000000000 2:4089750000000000 3:4021800000000000
result host=h1,job=x ; 1:7ff8000000000001 2:7ff8000000000001 3:7ff8000000000001
result host=h2,job=x,zone=z1 ; 3:7ff8000000000001
result host=h1,zone=z1 ; 1:7ff8000000000001 2:7ff8000000000001 4:7ff8000000000001
result host=h2 ; 0:7ff8000000000001 1:7ff8000000000001 2:7ff8000000000001 3:7ff8000000000001 4:7ff8000000000001
end
case binary - false on zone right job -2.5 0
grid 100000 10000 5
@@ -1373,11 +1383,11 @@ series __name__=b,host=h2,job=x,zone=z1 ;
series __name__=b,host=h2,job=y,zone=z1 ; 0:c0706a0000000000 1:4088500000000000
series __name__=b,host=h1,job=x ; 3:c04cb00000000000
series __name__=b,host=h2,job=x ; 4:7ff8000000000001
result host=h1 ; 3:404cb00000000000
result host=h1 ; 3:404b700000000000
result host=h2,job=x ; 4:7ff8000000000001
result host=h2,job=y,zone=z1 ; 1:c074aa0000000000
result host=h2,zone=z1 ; 0:40706a0000000000
result job=y,zone=z1 ; 2:408f130000000000 3:7ff8000000000001 4:fff0000000000000
result host=h2,zone=z1 ; 0:4070420000000000
result job=y,zone=z1 ; 2:408eff0000000000 3:7ff8000000000001 4:fff0000000000000
end
case binary <= true ignoring host left - 0 1
grid 100000 10000 1
@@ -1500,6 +1510,7 @@ case binary * false ignoring host,zone right job,zone - 1
grid 100000 10000 1
series __name__=a,host=h2,job=x ; 0:4070d80000000000
series __name__=b,host=h2,job=y ;
result job=x ; 0:4070d80000000000
end
case binary % false on - left host - -2.5
grid 100000 10000 8
@@ -1590,6 +1601,7 @@ series __name__=a,host=h2,job=y ; 4:c08e420000000000 5:407ae20000000000 6:7ff800
series __name__=a,host=h1,job=y ; 2:c08bf20000000000
series __name__=b,host=h1,job=x ; 4:4051c00000000000
result host=h1,job=x ; 4:3ff0000000000000
result job=y ; 2:3ff0000000000000 4:3ff0000000000000 5:0 6:0
end
case binary - false on zone left job NaN -
grid 100000 10000 3
@@ -1652,6 +1664,9 @@ series __name__=a,host=h2,job=y ; 2:7ff8000000000001 4:40821e0000000000
series __name__=b,host=h2,job=y ; 7:c08a5d0000000000
series __name__=b,host=h1,job=y ; 1:408bd70000000000 2:4056380000000000 3:fff0000000000000
series __name__=b,host=h2,job=y,zone=z2 ; 7:4088e20000000000 8:4080ad0000000000
result __name__=b,host=h1,job=y ; 1:7ff0000000000000 2:7ff0000000000000 3:7ff0000000000000
result __name__=b,host=h2,job=y ; 7:7ff0000000000000
result __name__=b,host=h2,job=y,zone=z2 ; 7:7ff0000000000000 8:7ff0000000000000
end
case binary % false on job left - -2.5 -2.5
grid 100000 10000 8
@@ -1726,9 +1741,11 @@ series __name__=a,host=h1,job=y,zone=z1 ; 4:4080f70000000000 5:c058380000000000
series __name__=b,host=h2,job=y ; 5:4084e80000000000 6:7ff8000000000001 7:40874b0000000000
series __name__=b,host=h2,job=x,zone=z2 ; 0:4072760000000000 3:40809b0000000000 5:7ff8000000000001
series __name__=b,host=h2,job=x ; 4:c064a00000000000 5:fff0000000000000
result host=h2,job=x ; 4:0 5:7ff8000000000001
result host=h2,job=x,zone=z2 ; 0:7ff8000000000001 3:7ff8000000000001 5:7ff8000000000001
result host=h2,job=y ; 5:7ff8000000000001 6:7ff8000000000001 7:7ff8000000000001
result host=h1,job=x ; 2:7ff8000000000001 3:7ff8000000000001 4:7ff8000000000001 5:7ff8000000000001 7:7ff8000000000001
result host=h1,job=y,zone=z1 ; 4:7ff8000000000001 5:7ff8000000000001
result host=h2,job=x ; 4:0
result host=h2,job=x,zone=z1 ; 1:7ff8000000000001 2:7ff8000000000001 3:7ff8000000000001 4:7ff8000000000001 5:7ff8000000000001
result host=h2,job=y ; 6:7ff8000000000001
end
case binary - false ignoring host,zone left host,job,zone - 1
grid 100000 10000 10
+2 -2
View File
@@ -703,8 +703,8 @@ fn vector_operations_match_prometheus() {
);
}
/// Binary operations with fill modifiers, evaluated by the Prometheus v3.15.0 engine; see the
/// header of `fill_cases.txt`.
/// Binary operations with fill modifiers, evaluated by the Prometheus engine named in the header of
/// `fill_cases.txt`.
#[test]
fn fill_modifiers_match_prometheus() {
let (matched, failed) = check_cases(include_str!("fill_cases.txt"));