test(promql): fold range_presence_null into null_samples

#9104 landed its own NULL-sample case whose data is the same series this
one already used: one host with interior NULLs, one host with nothing
but NULLs. Keeping both means two files asserting the same semantics on
the same rows.

The merged case keeps every query from both, so the presence functions
still cover the trailing-NULL window, the count of two, the empty
left-open window at t=7, and the all-NULL windows.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
This commit is contained in:
Dennis Zhuang
2026-09-11 21:00:51 +08:00
parent a164c4a66d
commit d0aa4ecb7b
4 changed files with 153 additions and 250 deletions
@@ -1,57 +1,100 @@
-- NULL fields are missing samples, not zero-valued samples.
CREATE TABLE null_samples (
ts TIMESTAMP(3) TIME INDEX,
"region" STRING,
host STRING,
host STRING PRIMARY KEY,
val DOUBLE,
PRIMARY KEY ("region", host)
);
Affected Rows: 0
INSERT INTO null_samples VALUES
(0, 'empty', 'e', NULL),
(1000, 'empty', 'e', NULL),
(0, 'ok', 'o', 1.0),
(1000, 'ok', 'o', 2.0),
(2000, 'ok', 'o', 3.0);
Affected Rows: 5
-- The `empty` series has no sample in any window, so it must not reach the aggregation.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 15, '1s') avg by (region) (rate(null_samples[4s]));
+--------+---------------------+---------------------------------------------+
| region | ts | avg(prom_rate(ts_range,val,ts,Int64(4000))) |
+--------+---------------------+---------------------------------------------+
| ok | 1970-01-01T00:00:01 | 0.375 |
| ok | 1970-01-01T00:00:02 | 0.625 |
| ok | 1970-01-01T00:00:03 | 1.0 |
| ok | 1970-01-01T00:00:04 | 0.625 |
+--------+---------------------+---------------------------------------------+
DROP TABLE null_samples;
Affected Rows: 0
CREATE TABLE sparse_samples (
ts TIMESTAMP(3) TIME INDEX,
host STRING PRIMARY KEY,
val DOUBLE
);
Affected Rows: 0
INSERT INTO sparse_samples VALUES
(0, 'a', 1.0),
(1000, 'a', NULL),
(2000, 'a', NULL),
(3000, 'a', 4.0);
(3000, 'a', 4.0),
(0, 'b', NULL),
(1000, 'b', NULL),
(2000, 'b', NULL),
(3000, 'b', NULL);
Affected Rows: 4
Affected Rows: 8
-- Every window below holds two samples, 1.0 at 0s and 4.0 at 3s.
TQL EVAL (3, 3, '1s') rate(sparse_samples[4s]);
-- At t=2 the trailing NULLs must not hide 1; at t=3 count must be 2.
-- At t=7 the left-open window is empty. Valid results must disappear.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') count_over_time(null_samples{host="a"}[4s]);
+---------------------+------------------------------------+------+
| ts | prom_count_over_time(ts_range,val) | host |
+---------------------+------------------------------------+------+
| 1970-01-01T00:00:02 | 1.0 | a |
| 1970-01-01T00:00:03 | 2.0 | a |
| 1970-01-01T00:00:04 | 1.0 | a |
| 1970-01-01T00:00:05 | 1.0 | a |
| 1970-01-01T00:00:06 | 1.0 | a |
+---------------------+------------------------------------+------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') last_over_time(null_samples{host="a"}[4s]);
+---------------------+-----------------------------------+------+
| ts | prom_last_over_time(ts_range,val) | host |
+---------------------+-----------------------------------+------+
| 1970-01-01T00:00:02 | 1.0 | a |
| 1970-01-01T00:00:03 | 4.0 | a |
| 1970-01-01T00:00:04 | 4.0 | a |
| 1970-01-01T00:00:05 | 4.0 | a |
| 1970-01-01T00:00:06 | 4.0 | a |
+---------------------+-----------------------------------+------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') present_over_time(null_samples{host="a"}[4s]);
+---------------------+--------------------------------------+------+
| ts | prom_present_over_time(ts_range,val) | host |
+---------------------+--------------------------------------+------+
| 1970-01-01T00:00:02 | 1.0 | a |
| 1970-01-01T00:00:03 | 1.0 | a |
| 1970-01-01T00:00:04 | 1.0 | a |
| 1970-01-01T00:00:05 | 1.0 | a |
| 1970-01-01T00:00:06 | 1.0 | a |
+---------------------+--------------------------------------+------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') absent_over_time(null_samples{host="a"}[4s]);
+---------------------+-------------------------------------+------+
| ts | prom_absent_over_time(ts_range,val) | host |
+---------------------+-------------------------------------+------+
| 1970-01-01T00:00:07 | 1.0 | a |
+---------------------+-------------------------------------+------+
-- All-NULL windows have no samples: only absent_over_time returns 1.
TQL EVAL (3, 3, '1s') count_over_time(null_samples{host="b"}[4s]);
++
++
TQL EVAL (3, 3, '1s') last_over_time(null_samples{host="b"}[4s]);
++
++
TQL EVAL (3, 3, '1s') present_over_time(null_samples{host="b"}[4s]);
++
++
TQL EVAL (3, 3, '1s') absent_over_time(null_samples{host="b"}[4s]);
+---------------------+-------------------------------------+------+
| ts | prom_absent_over_time(ts_range,val) | host |
+---------------------+-------------------------------------+------+
| 1970-01-01T00:00:03 | 1.0 | b |
+---------------------+-------------------------------------+------+
-- Every function below sees the same two samples, 1.0 at 0s and 4.0 at 3s.
TQL EVAL (3, 3, '1s') rate(null_samples{host="a"}[4s]);
+---------------------+----------------------------------------+------+
| ts | prom_rate(ts_range,val,ts,Int64(4000)) | host |
@@ -59,7 +102,7 @@ TQL EVAL (3, 3, '1s') rate(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 1.0 | a |
+---------------------+----------------------------------------+------+
TQL EVAL (3, 3, '1s') increase(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') increase(null_samples{host="a"}[4s]);
+---------------------+--------------------------------------------+------+
| ts | prom_increase(ts_range,val,ts,Int64(4000)) | host |
@@ -67,7 +110,7 @@ TQL EVAL (3, 3, '1s') increase(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 4.0 | a |
+---------------------+--------------------------------------------+------+
TQL EVAL (3, 3, '1s') delta(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') delta(null_samples{host="a"}[4s]);
+---------------------+-----------------------------------------+------+
| ts | prom_delta(ts_range,val,ts,Int64(4000)) | host |
@@ -75,7 +118,7 @@ TQL EVAL (3, 3, '1s') delta(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 4.0 | a |
+---------------------+-----------------------------------------+------+
TQL EVAL (3, 3, '1s') idelta(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') idelta(null_samples{host="a"}[4s]);
+---------------------+---------------------------+------+
| ts | prom_idelta(ts_range,val) | host |
@@ -83,7 +126,7 @@ TQL EVAL (3, 3, '1s') idelta(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 3.0 | a |
+---------------------+---------------------------+------+
TQL EVAL (3, 3, '1s') irate(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') irate(null_samples{host="a"}[4s]);
+---------------------+--------------------------+------+
| ts | prom_irate(ts_range,val) | host |
@@ -91,7 +134,7 @@ TQL EVAL (3, 3, '1s') irate(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 1.0 | a |
+---------------------+--------------------------+------+
TQL EVAL (3, 3, '1s') changes(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') changes(null_samples{host="a"}[4s]);
+---------------------+----------------------------+------+
| ts | prom_changes(ts_range,val) | host |
@@ -99,7 +142,7 @@ TQL EVAL (3, 3, '1s') changes(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 1.0 | a |
+---------------------+----------------------------+------+
TQL EVAL (3, 3, '1s') resets(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') resets(null_samples{host="a"}[4s]);
+---------------------+---------------------------+------+
| ts | prom_resets(ts_range,val) | host |
@@ -107,7 +150,7 @@ TQL EVAL (3, 3, '1s') resets(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 0.0 | a |
+---------------------+---------------------------+------+
TQL EVAL (3, 3, '1s') avg_over_time(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') avg_over_time(null_samples{host="a"}[4s]);
+---------------------+----------------------------------+------+
| ts | prom_avg_over_time(ts_range,val) | host |
@@ -115,7 +158,7 @@ TQL EVAL (3, 3, '1s') avg_over_time(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 2.5 | a |
+---------------------+----------------------------------+------+
TQL EVAL (3, 3, '1s') stddev_over_time(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') stddev_over_time(null_samples{host="a"}[4s]);
+---------------------+-------------------------------------+------+
| ts | prom_stddev_over_time(ts_range,val) | host |
@@ -123,7 +166,7 @@ TQL EVAL (3, 3, '1s') stddev_over_time(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 1.5 | a |
+---------------------+-------------------------------------+------+
TQL EVAL (3, 3, '1s') stdvar_over_time(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') stdvar_over_time(null_samples{host="a"}[4s]);
+---------------------+-------------------------------------+------+
| ts | prom_stdvar_over_time(ts_range,val) | host |
@@ -131,7 +174,7 @@ TQL EVAL (3, 3, '1s') stdvar_over_time(sparse_samples[4s]);
| 1970-01-01T00:00:03 | 2.25 | a |
+---------------------+-------------------------------------+------+
TQL EVAL (3, 3, '1s') quantile_over_time(0.5, sparse_samples[4s]);
TQL EVAL (3, 3, '1s') quantile_over_time(0.5, null_samples{host="a"}[4s]);
+---------------------+----------------------------------------------------+------+
| ts | prom_quantile_over_time(ts_range,val,Float64(0.5)) | host |
@@ -139,24 +182,29 @@ TQL EVAL (3, 3, '1s') quantile_over_time(0.5, sparse_samples[4s]);
| 1970-01-01T00:00:03 | 2.5 | a |
+---------------------+----------------------------------------------------+------+
-- A window whose only slot is NULL holds no sample.
TQL EVAL (1, 1, '1s') rate(sparse_samples[1s]);
++
++
TQL EVAL (1, 1, '1s') changes(sparse_samples[1s]);
-- A window whose only slot is NULL holds no sample, like a window with no row.
TQL EVAL (1, 1, '1s') rate(null_samples{host="a"}[1s]);
++
++
-- Prometheus returns an empty vector for a range without samples, not NaN.
TQL EVAL (1, 1, '1s') quantile_over_time(0.5, sparse_samples[1s]);
TQL EVAL (1, 1, '1s') quantile_over_time(0.5, null_samples{host="a"}[1s]);
++
++
DROP TABLE sparse_samples;
-- `b` never has a sample, so it must not reach the aggregation.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 15, '1s') avg by (host) (rate(null_samples[4s]));
+------+---------------------+---------------------------------------------+
| host | ts | avg(prom_rate(ts_range,val,ts,Int64(4000))) |
+------+---------------------+---------------------------------------------+
| a | 1970-01-01T00:00:03 | 1.0 |
+------+---------------------+---------------------------------------------+
DROP TABLE null_samples;
Affected Rows: 0
@@ -177,18 +225,8 @@ INSERT INTO multi_field VALUES
Affected Rows: 4
-- f1 and f2 share the same rows but have samples at different timestamps. Dropping the rows
-- where f2 is NULL would also drop two of f1's samples, so each field is counted separately.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (3, 3, '1s') rate(multi_field[4s]);
+---------------------+---------------------------------------+---------------------------------------+------+
| ts | prom_rate(ts_range,f1,ts,Int64(4000)) | prom_rate(ts_range,f2,ts,Int64(4000)) | host |
+---------------------+---------------------------------------+---------------------------------------+------+
| 1970-01-01T00:00:03 | 1.0 | 10.0 | a |
+---------------------+---------------------------------------+---------------------------------------+------+
-- f1 has two samples in this window and f2 only one, so f1 keeps its result while f2 is NULL.
-- Dropping the whole row would take f1's samples with it.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (1, 1, '1s') rate(multi_field[4s]);
@@ -1,68 +1,71 @@
-- NULL fields are missing samples, not zero-valued samples.
CREATE TABLE null_samples (
ts TIMESTAMP(3) TIME INDEX,
"region" STRING,
host STRING,
host STRING PRIMARY KEY,
val DOUBLE,
PRIMARY KEY ("region", host)
);
INSERT INTO null_samples VALUES
(0, 'empty', 'e', NULL),
(1000, 'empty', 'e', NULL),
(0, 'ok', 'o', 1.0),
(1000, 'ok', 'o', 2.0),
(2000, 'ok', 'o', 3.0);
-- The `empty` series has no sample in any window, so it must not reach the aggregation.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 15, '1s') avg by (region) (rate(null_samples[4s]));
DROP TABLE null_samples;
CREATE TABLE sparse_samples (
ts TIMESTAMP(3) TIME INDEX,
host STRING PRIMARY KEY,
val DOUBLE
);
INSERT INTO sparse_samples VALUES
(0, 'a', 1.0),
(1000, 'a', NULL),
(2000, 'a', NULL),
(3000, 'a', 4.0);
(3000, 'a', 4.0),
(0, 'b', NULL),
(1000, 'b', NULL),
(2000, 'b', NULL),
(3000, 'b', NULL);
-- Every window below holds two samples, 1.0 at 0s and 4.0 at 3s.
TQL EVAL (3, 3, '1s') rate(sparse_samples[4s]);
-- At t=2 the trailing NULLs must not hide 1; at t=3 count must be 2.
-- At t=7 the left-open window is empty. Valid results must disappear.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') count_over_time(null_samples{host="a"}[4s]);
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') last_over_time(null_samples{host="a"}[4s]);
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') present_over_time(null_samples{host="a"}[4s]);
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') absent_over_time(null_samples{host="a"}[4s]);
TQL EVAL (3, 3, '1s') increase(sparse_samples[4s]);
-- All-NULL windows have no samples: only absent_over_time returns 1.
TQL EVAL (3, 3, '1s') count_over_time(null_samples{host="b"}[4s]);
TQL EVAL (3, 3, '1s') last_over_time(null_samples{host="b"}[4s]);
TQL EVAL (3, 3, '1s') present_over_time(null_samples{host="b"}[4s]);
TQL EVAL (3, 3, '1s') absent_over_time(null_samples{host="b"}[4s]);
TQL EVAL (3, 3, '1s') delta(sparse_samples[4s]);
-- Every function below sees the same two samples, 1.0 at 0s and 4.0 at 3s.
TQL EVAL (3, 3, '1s') rate(null_samples{host="a"}[4s]);
TQL EVAL (3, 3, '1s') idelta(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') increase(null_samples{host="a"}[4s]);
TQL EVAL (3, 3, '1s') irate(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') delta(null_samples{host="a"}[4s]);
TQL EVAL (3, 3, '1s') changes(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') idelta(null_samples{host="a"}[4s]);
TQL EVAL (3, 3, '1s') resets(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') irate(null_samples{host="a"}[4s]);
TQL EVAL (3, 3, '1s') avg_over_time(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') changes(null_samples{host="a"}[4s]);
TQL EVAL (3, 3, '1s') stddev_over_time(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') resets(null_samples{host="a"}[4s]);
TQL EVAL (3, 3, '1s') stdvar_over_time(sparse_samples[4s]);
TQL EVAL (3, 3, '1s') avg_over_time(null_samples{host="a"}[4s]);
TQL EVAL (3, 3, '1s') quantile_over_time(0.5, sparse_samples[4s]);
TQL EVAL (3, 3, '1s') stddev_over_time(null_samples{host="a"}[4s]);
-- A window whose only slot is NULL holds no sample.
TQL EVAL (1, 1, '1s') rate(sparse_samples[1s]);
TQL EVAL (3, 3, '1s') stdvar_over_time(null_samples{host="a"}[4s]);
TQL EVAL (1, 1, '1s') changes(sparse_samples[1s]);
TQL EVAL (3, 3, '1s') quantile_over_time(0.5, null_samples{host="a"}[4s]);
-- A window whose only slot is NULL holds no sample, like a window with no row.
TQL EVAL (1, 1, '1s') rate(null_samples{host="a"}[1s]);
-- Prometheus returns an empty vector for a range without samples, not NaN.
TQL EVAL (1, 1, '1s') quantile_over_time(0.5, sparse_samples[1s]);
TQL EVAL (1, 1, '1s') quantile_over_time(0.5, null_samples{host="a"}[1s]);
DROP TABLE sparse_samples;
-- `b` never has a sample, so it must not reach the aggregation.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (0, 15, '1s') avg by (host) (rate(null_samples[4s]));
DROP TABLE null_samples;
CREATE TABLE multi_field (
ts TIMESTAMP(3) TIME INDEX,
@@ -77,12 +80,8 @@ INSERT INTO multi_field VALUES
(2000, 'a', 3.0, NULL),
(3000, 'a', 4.0, 40.0);
-- f1 and f2 share the same rows but have samples at different timestamps. Dropping the rows
-- where f2 is NULL would also drop two of f1's samples, so each field is counted separately.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (3, 3, '1s') rate(multi_field[4s]);
-- f1 has two samples in this window and f2 only one, so f1 keeps its result while f2 is NULL.
-- Dropping the whole row would take f1's samples with it.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (1, 1, '1s') rate(multi_field[4s]);
@@ -1,99 +0,0 @@
-- NULL fields are missing samples, not zero-valued samples.
CREATE TABLE range_presence_null (
ts TIMESTAMP(3) TIME INDEX,
host STRING PRIMARY KEY,
val DOUBLE,
);
Affected Rows: 0
INSERT INTO range_presence_null VALUES
(0, 'a', 1.0),
(1000, 'a', NULL),
(2000, 'a', NULL),
(3000, 'a', 4.0),
(0, 'b', NULL),
(1000, 'b', NULL),
(2000, 'b', NULL),
(3000, 'b', NULL);
Affected Rows: 8
-- At t=2 the trailing NULLs must not hide 1; at t=3 count must be 2.
-- At t=7 the left-open window is empty. Valid results must disappear.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') count_over_time(range_presence_null{host="a"}[4s]);
+---------------------+------------------------------------+------+
| ts | prom_count_over_time(ts_range,val) | host |
+---------------------+------------------------------------+------+
| 1970-01-01T00:00:02 | 1.0 | a |
| 1970-01-01T00:00:03 | 2.0 | a |
| 1970-01-01T00:00:04 | 1.0 | a |
| 1970-01-01T00:00:05 | 1.0 | a |
| 1970-01-01T00:00:06 | 1.0 | a |
+---------------------+------------------------------------+------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') last_over_time(range_presence_null{host="a"}[4s]);
+---------------------+-----------------------------------+------+
| ts | prom_last_over_time(ts_range,val) | host |
+---------------------+-----------------------------------+------+
| 1970-01-01T00:00:02 | 1.0 | a |
| 1970-01-01T00:00:03 | 4.0 | a |
| 1970-01-01T00:00:04 | 4.0 | a |
| 1970-01-01T00:00:05 | 4.0 | a |
| 1970-01-01T00:00:06 | 4.0 | a |
+---------------------+-----------------------------------+------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') present_over_time(range_presence_null{host="a"}[4s]);
+---------------------+--------------------------------------+------+
| ts | prom_present_over_time(ts_range,val) | host |
+---------------------+--------------------------------------+------+
| 1970-01-01T00:00:02 | 1.0 | a |
| 1970-01-01T00:00:03 | 1.0 | a |
| 1970-01-01T00:00:04 | 1.0 | a |
| 1970-01-01T00:00:05 | 1.0 | a |
| 1970-01-01T00:00:06 | 1.0 | a |
+---------------------+--------------------------------------+------+
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') absent_over_time(range_presence_null{host="a"}[4s]);
+---------------------+-------------------------------------+------+
| ts | prom_absent_over_time(ts_range,val) | host |
+---------------------+-------------------------------------+------+
| 1970-01-01T00:00:07 | 1.0 | a |
+---------------------+-------------------------------------+------+
-- All-NULL windows have no samples: only absent_over_time returns 1.
TQL EVAL (3, 3, '1s') count_over_time(range_presence_null{host="b"}[4s]);
++
++
TQL EVAL (3, 3, '1s') last_over_time(range_presence_null{host="b"}[4s]);
++
++
TQL EVAL (3, 3, '1s') present_over_time(range_presence_null{host="b"}[4s]);
++
++
TQL EVAL (3, 3, '1s') absent_over_time(range_presence_null{host="b"}[4s]);
+---------------------+-------------------------------------+------+
| ts | prom_absent_over_time(ts_range,val) | host |
+---------------------+-------------------------------------+------+
| 1970-01-01T00:00:03 | 1.0 | b |
+---------------------+-------------------------------------+------+
DROP TABLE range_presence_null;
Affected Rows: 0
@@ -1,35 +0,0 @@
-- NULL fields are missing samples, not zero-valued samples.
CREATE TABLE range_presence_null (
ts TIMESTAMP(3) TIME INDEX,
host STRING PRIMARY KEY,
val DOUBLE,
);
INSERT INTO range_presence_null VALUES
(0, 'a', 1.0),
(1000, 'a', NULL),
(2000, 'a', NULL),
(3000, 'a', 4.0),
(0, 'b', NULL),
(1000, 'b', NULL),
(2000, 'b', NULL),
(3000, 'b', NULL);
-- At t=2 the trailing NULLs must not hide 1; at t=3 count must be 2.
-- At t=7 the left-open window is empty. Valid results must disappear.
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') count_over_time(range_presence_null{host="a"}[4s]);
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') last_over_time(range_presence_null{host="a"}[4s]);
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') present_over_time(range_presence_null{host="a"}[4s]);
-- SQLNESS SORT_RESULT 3 1
TQL EVAL (2, 7, '1s') absent_over_time(range_presence_null{host="a"}[4s]);
-- All-NULL windows have no samples: only absent_over_time returns 1.
TQL EVAL (3, 3, '1s') count_over_time(range_presence_null{host="b"}[4s]);
TQL EVAL (3, 3, '1s') last_over_time(range_presence_null{host="b"}[4s]);
TQL EVAL (3, 3, '1s') present_over_time(range_presence_null{host="b"}[4s]);
TQL EVAL (3, 3, '1s') absent_over_time(range_presence_null{host="b"}[4s]);
DROP TABLE range_presence_null;