From 9bf1badc2058bea6fe9e629794eb6a7d5ccca0dd Mon Sep 17 00:00:00 2001 From: discord9 <55937128+discord9@users.noreply.github.com> Date: Tue, 8 Sep 2026 19:12:01 +0800 Subject: [PATCH] test(promql): verify preserved selector output order Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> --- .../promql/native_time_selection.result | 160 +++++++++--------- .../common/promql/precisions.result | 136 +++++++-------- 2 files changed, 150 insertions(+), 146 deletions(-) diff --git a/tests/cases/standalone/common/promql/native_time_selection.result b/tests/cases/standalone/common/promql/native_time_selection.result index 5f3a56c021..1715356f81 100644 --- a/tests/cases/standalone/common/promql/native_time_selection.result +++ b/tests/cases/standalone/common/promql/native_time_selection.result @@ -47,11 +47,11 @@ ADMIN FLUSH_TABLE('native_time_us'); -- At 1s, selection keeps an exact native timestamp. TQL EVAL (1, 1, '1s', '300s') native_time_us{series="exact"}; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:01 | exact | 201.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 201.0 | exact | 1970-01-01T00:00:01 | ++-------+--------+---------------------+ TQL EVAL (1, 1, '1s', '300s') timestamp(native_time_us{series="future"}); @@ -69,63 +69,63 @@ TQL EVAL (1, 1, '1s', '300s') timestamp(native_time_us{series="exact"}); -- Instant lookback bounds are exclusive: these return only 302 and 702. TQL EVAL (1, 1, '1s', '300s') native_time_us{series=~"lower.*"}; -+---------------------+-----------+-------+ -| ts | series | val | -+---------------------+-----------+-------+ -| 1970-01-01T00:00:01 | lowerplus | 302.0 | -+---------------------+-----------+-------+ ++-------+-----------+---------------------+ +| val | series | ts | ++-------+-----------+---------------------+ +| 302.0 | lowerplus | 1970-01-01T00:00:01 | ++-------+-----------+---------------------+ TQL EVAL (301, 301, '1s', '300s') native_time_us{series=~"positive_lower.*"}; -+---------------------+--------------------+-------+ -| ts | series | val | -+---------------------+--------------------+-------+ -| 1970-01-01T00:05:01 | positive_lowerplus | 702.0 | -+---------------------+--------------------+-------+ ++-------+--------------------+---------------------+ +| val | series | ts | ++-------+--------------------+---------------------+ +| 702.0 | positive_lowerplus | 1970-01-01T00:05:01 | ++-------+--------------------+---------------------+ -- The sub-millisecond point belongs only to the 2s evaluation step. -- SQLNESS SORT_RESULT 3 1 TQL EVAL (1, 2, '1s', '300s') native_time_us{series="multi"}; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:02 | multi | 401.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 401.0 | multi | 1970-01-01T00:00:02 | ++-------+--------+---------------------+ -- The latest native timestamp below 1s is retained even when inserts are unordered. TQL EVAL (1, 1, '1s', '300s') native_time_us{series="past"}; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:01 | past | 602.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 602.0 | past | 1970-01-01T00:00:01 | ++-------+--------+---------------------+ -- Offsets select native timestamps, including stored negative time. TQL EVAL (0, 0, '1s', '300s') native_time_us{series="offset"}; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:00 | offset | 502.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 502.0 | offset | 1970-01-01T00:00:00 | ++-------+--------+---------------------+ TQL EVAL (0, 0, '1s', '300s') native_time_us{series="offset"} offset 1s; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:00 | offset | 501.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 501.0 | offset | 1970-01-01T00:00:00 | ++-------+--------+---------------------+ TQL EVAL (0, 0, '1s', '300s') native_time_us{series="offset"} offset -1s; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:00 | offset | 503.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 503.0 | offset | 1970-01-01T00:00:00 | ++-------+--------+---------------------+ -- [1s] at 1s excludes 0 and 1s+tick, retaining 0+tick, 0+2ticks, and 1s. TQL EVAL (1, 1, '1s', '300s') count_over_time(native_time_us{series="window"}[1s]); @@ -212,11 +212,11 @@ ADMIN FLUSH_TABLE('native_time_ns'); -- At 1s, selection keeps an exact native timestamp. TQL EVAL (1, 1, '1s', '300s') native_time_ns{series="exact"}; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:01 | exact | 201.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 201.0 | exact | 1970-01-01T00:00:01 | ++-------+--------+---------------------+ TQL EVAL (1, 1, '1s', '300s') timestamp(native_time_ns{series="future"}); @@ -234,63 +234,63 @@ TQL EVAL (1, 1, '1s', '300s') timestamp(native_time_ns{series="exact"}); -- Instant lookback bounds are exclusive: these return only 302 and 702. TQL EVAL (1, 1, '1s', '300s') native_time_ns{series=~"lower.*"}; -+---------------------+-----------+-------+ -| ts | series | val | -+---------------------+-----------+-------+ -| 1970-01-01T00:00:01 | lowerplus | 302.0 | -+---------------------+-----------+-------+ ++-------+-----------+---------------------+ +| val | series | ts | ++-------+-----------+---------------------+ +| 302.0 | lowerplus | 1970-01-01T00:00:01 | ++-------+-----------+---------------------+ TQL EVAL (301, 301, '1s', '300s') native_time_ns{series=~"positive_lower.*"}; -+---------------------+--------------------+-------+ -| ts | series | val | -+---------------------+--------------------+-------+ -| 1970-01-01T00:05:01 | positive_lowerplus | 702.0 | -+---------------------+--------------------+-------+ ++-------+--------------------+---------------------+ +| val | series | ts | ++-------+--------------------+---------------------+ +| 702.0 | positive_lowerplus | 1970-01-01T00:05:01 | ++-------+--------------------+---------------------+ -- The sub-millisecond point belongs only to the 2s evaluation step. -- SQLNESS SORT_RESULT 3 1 TQL EVAL (1, 2, '1s', '300s') native_time_ns{series="multi"}; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:02 | multi | 401.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 401.0 | multi | 1970-01-01T00:00:02 | ++-------+--------+---------------------+ -- The latest native timestamp below 1s is retained even when inserts are unordered. TQL EVAL (1, 1, '1s', '300s') native_time_ns{series="past"}; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:01 | past | 602.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 602.0 | past | 1970-01-01T00:00:01 | ++-------+--------+---------------------+ -- Offsets select native timestamps, including stored negative time. TQL EVAL (0, 0, '1s', '300s') native_time_ns{series="offset"}; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:00 | offset | 502.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 502.0 | offset | 1970-01-01T00:00:00 | ++-------+--------+---------------------+ TQL EVAL (0, 0, '1s', '300s') native_time_ns{series="offset"} offset 1s; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:00 | offset | 501.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 501.0 | offset | 1970-01-01T00:00:00 | ++-------+--------+---------------------+ TQL EVAL (0, 0, '1s', '300s') native_time_ns{series="offset"} offset -1s; -+---------------------+--------+-------+ -| ts | series | val | -+---------------------+--------+-------+ -| 1970-01-01T00:00:00 | offset | 503.0 | -+---------------------+--------+-------+ ++-------+--------+---------------------+ +| val | series | ts | ++-------+--------+---------------------+ +| 503.0 | offset | 1970-01-01T00:00:00 | ++-------+--------+---------------------+ -- [1s] at 1s excludes 0 and 1s+tick, retaining 0+tick, 0+2ticks, and 1s. TQL EVAL (1, 1, '1s', '300s') count_over_time(native_time_ns{series="window"}[1s]); diff --git a/tests/cases/standalone/common/promql/precisions.result b/tests/cases/standalone/common/promql/precisions.result index 963a6ab88b..4026a35832 100644 --- a/tests/cases/standalone/common/promql/precisions.result +++ b/tests/cases/standalone/common/promql/precisions.result @@ -86,14 +86,14 @@ TQL EVAL (0, 15, '5s') avg_over_time(host_sec{host="host1"}[5s]); -- SQLNESS SORT_RESULT 3 1 TQL EVAL (0, 15, '5s') host_micro{host="host1"}; -+---------------------+-------+-----+ -| ts | host | val | -+---------------------+-------+-----+ -| 1970-01-01T00:00:00 | host1 | 1.0 | -| 1970-01-01T00:00:05 | host1 | 3.0 | -| 1970-01-01T00:00:10 | host1 | 5.0 | -| 1970-01-01T00:00:15 | host1 | 7.0 | -+---------------------+-------+-----+ ++-----+-------+---------------------+ +| val | host | ts | ++-----+-------+---------------------+ +| 1.0 | host1 | 1970-01-01T00:00:00 | +| 3.0 | host1 | 1970-01-01T00:00:05 | +| 5.0 | host1 | 1970-01-01T00:00:10 | +| 7.0 | host1 | 1970-01-01T00:00:15 | ++-----+-------+---------------------+ -- SQLNESS SORT_RESULT 3 1 TQL EVAL (0, 15, '5s') avg_over_time(host_micro{host="host1"}[5s]); @@ -143,20 +143,21 @@ TQL EVAL (0, 15, '5s') avg_over_time(host_sec{host="host1"}[5s]) + avg_over_time -- SQLNESS REPLACE host_nano.__table_id\s*=\s*UInt32\(\d+\) host_nano.__table_id=UInt32(REDACTED) TQL EXPLAIN (0, 10, '5s') host_micro{host="host1"}; -+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| plan_type | plan | -+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| logical_plan | MergeScan [is_placeholder=false, remote_input=[ | -| | PromInstantManipulate: range=[0..10000], lookback=[300000], interval=[5000], time index=[ts] | -| | PromSeriesDivide: tags=["host"] | -| | Sort: host_micro.host ASC NULLS FIRST, host_micro.ts ASC NULLS FIRST | -| | Filter: host_micro.host = Utf8("host1") AND host_micro.ts > TimestampMicrosecond(-300000000, None) AND host_micro.ts <= TimestampMicrosecond(10000000, None) | -| | TableScan: host_micro, partial_filters=[host_micro.host = Utf8("host1"), host_micro.ts > TimestampMicrosecond(-300000000, None), host_micro.ts <= TimestampMicrosecond(10000000, None)] | -| | ]] | -| physical_plan | CooperativeExec | ++---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| plan_type | plan | ++---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| logical_plan | MergeScan [is_placeholder=false, remote_input=[ | +| | PromInstantManipulate: range=[0..10000], lookback=[300000], interval=[5000], time index=[ts] | +| | PromSeriesDivide: tags=["host"] | +| | Sort: host_micro.host ASC NULLS FIRST, host_micro.ts ASC NULLS FIRST | +| | Projection: host_micro.val, host_micro.host, host_micro.ts | +| | Filter: host_micro.host = Utf8("host1") AND host_micro.ts > TimestampMicrosecond(-300000000, None) AND host_micro.ts <= TimestampMicrosecond(10000000, None) | +| | TableScan: host_micro, partial_filters=[host_micro.host = Utf8("host1"), host_micro.ts > TimestampMicrosecond(-300000000, None), host_micro.ts <= TimestampMicrosecond(10000000, None)] | +| | ]] | +| physical_plan | CooperativeExec | | | MergeScanExec: REDACTED -| | | -+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| | | ++---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -- The same exclusive-lower, inclusive-upper window applies to nanosecond indexes. -- Expected native bounds: ts_ns > -300000000000ns AND ts_ns <= 10000000000ns. @@ -167,20 +168,21 @@ TQL EXPLAIN (0, 10, '5s') host_micro{host="host1"}; -- SQLNESS REPLACE host_nano.__table_id\s*=\s*UInt32\(\d+\) host_nano.__table_id=UInt32(REDACTED) TQL EXPLAIN (0, 10, '5s') host_nano{host="host1"}; -+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| plan_type | plan | -+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| logical_plan | MergeScan [is_placeholder=false, remote_input=[ | -| | PromInstantManipulate: range=[0..10000], lookback=[300000], interval=[5000], time index=[ts] | -| | PromSeriesDivide: tags=["host"] | -| | Sort: host_nano.host ASC NULLS FIRST, host_nano.ts ASC NULLS FIRST | -| | Filter: host_nano.host = Utf8("host1") AND host_nano.ts > TimestampNanosecond(-300000000000, None) AND host_nano.ts <= TimestampNanosecond(10000000000, None) | -| | TableScan: host_nano, partial_filters=[host_nano.host = Utf8("host1"), host_nano.ts > TimestampNanosecond(-300000000000, None), host_nano.ts <= TimestampNanosecond(10000000000, None)] | -| | ]] | -| physical_plan | CooperativeExec | ++---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| plan_type | plan | ++---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| logical_plan | MergeScan [is_placeholder=false, remote_input=[ | +| | PromInstantManipulate: range=[0..10000], lookback=[300000], interval=[5000], time index=[ts] | +| | PromSeriesDivide: tags=["host"] | +| | Sort: host_nano.host ASC NULLS FIRST, host_nano.ts ASC NULLS FIRST | +| | Projection: host_nano.val, host_nano.host, host_nano.ts | +| | Filter: host_nano.host = Utf8("host1") AND host_nano.ts > TimestampNanosecond(-300000000000, None) AND host_nano.ts <= TimestampNanosecond(10000000000, None) | +| | TableScan: host_nano, partial_filters=[host_nano.host = Utf8("host1"), host_nano.ts > TimestampNanosecond(-300000000000, None), host_nano.ts <= TimestampNanosecond(10000000000, None)] | +| | ]] | +| physical_plan | CooperativeExec | | | MergeScanExec: REDACTED -| | | -+---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| | | ++---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -- Range selectors use their range window instead of the default lookback. -- Native range selector filter for [5s]: @@ -192,23 +194,24 @@ TQL EXPLAIN (0, 10, '5s') host_nano{host="host1"}; -- SQLNESS REPLACE host_micro.__table_id\s*=\s*UInt32\(\d+\) host_micro.__table_id=UInt32(REDACTED) TQL EXPLAIN (0, 10, '5s') avg_over_time(host_micro{host="host1"}[5s]); -+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| plan_type | plan | -+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| logical_plan | MergeScan [is_placeholder=false, remote_input=[ | -| | Filter: prom_avg_over_time(ts_range,val) IS NOT NULL | -| | Projection: host_micro.ts, prom_avg_over_time(ts_range, val) AS prom_avg_over_time(ts_range,val), host_micro.host | -| | PromRangeManipulate: req range=[0..10000], interval=[5000], eval range=[5000], time index=[ts], values=["val"] | -| | PromSeriesNormalize: offset=[0], time index=[ts], filter NaN: [true] | -| | PromSeriesDivide: tags=["host"] | -| | Sort: host_micro.host ASC NULLS FIRST, host_micro.ts ASC NULLS FIRST | -| | Filter: host_micro.host = Utf8("host1") AND host_micro.ts > TimestampMicrosecond(-5000000, None) AND host_micro.ts <= TimestampMicrosecond(10000000, None) | -| | TableScan: host_micro, partial_filters=[host_micro.host = Utf8("host1"), host_micro.ts > TimestampMicrosecond(-5000000, None), host_micro.ts <= TimestampMicrosecond(10000000, None)] | -| | ]] | -| physical_plan | CooperativeExec | ++---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| plan_type | plan | ++---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| logical_plan | MergeScan [is_placeholder=false, remote_input=[ | +| | Filter: prom_avg_over_time(ts_range,val) IS NOT NULL | +| | Projection: host_micro.ts, prom_avg_over_time(ts_range, val) AS prom_avg_over_time(ts_range,val), host_micro.host | +| | PromRangeManipulate: req range=[0..10000], interval=[5000], eval range=[5000], time index=[ts], values=["val"] | +| | PromSeriesNormalize: offset=[0], time index=[ts], filter NaN: [true] | +| | PromSeriesDivide: tags=["host"] | +| | Sort: host_micro.host ASC NULLS FIRST, host_micro.ts ASC NULLS FIRST | +| | Projection: host_micro.val, host_micro.host, host_micro.ts | +| | Filter: host_micro.host = Utf8("host1") AND host_micro.ts > TimestampMicrosecond(-5000000, None) AND host_micro.ts <= TimestampMicrosecond(10000000, None) | +| | TableScan: host_micro, partial_filters=[host_micro.host = Utf8("host1"), host_micro.ts > TimestampMicrosecond(-5000000, None), host_micro.ts <= TimestampMicrosecond(10000000, None)] | +| | ]] | +| physical_plan | CooperativeExec | | | MergeScanExec: REDACTED -| | | -+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| | | ++---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -- Range membership also retains nanosecond precision. -- Expected native bounds: ts_ns > -5000000000ns AND ts_ns <= 10000000000ns. @@ -219,23 +222,24 @@ TQL EXPLAIN (0, 10, '5s') avg_over_time(host_micro{host="host1"}[5s]); -- SQLNESS REPLACE host_nano.__table_id\s*=\s*UInt32\(\d+\) host_nano.__table_id=UInt32(REDACTED) TQL EXPLAIN (0, 10, '5s') avg_over_time(host_nano{host="host1"}[5s]); -+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| plan_type | plan | -+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| logical_plan | MergeScan [is_placeholder=false, remote_input=[ | -| | Filter: prom_avg_over_time(ts_range,val) IS NOT NULL | -| | Projection: host_nano.ts, prom_avg_over_time(ts_range, val) AS prom_avg_over_time(ts_range,val), host_nano.host | -| | PromRangeManipulate: req range=[0..10000], interval=[5000], eval range=[5000], time index=[ts], values=["val"] | -| | PromSeriesNormalize: offset=[0], time index=[ts], filter NaN: [true] | -| | PromSeriesDivide: tags=["host"] | -| | Sort: host_nano.host ASC NULLS FIRST, host_nano.ts ASC NULLS FIRST | -| | Filter: host_nano.host = Utf8("host1") AND host_nano.ts > TimestampNanosecond(-5000000000, None) AND host_nano.ts <= TimestampNanosecond(10000000000, None) | -| | TableScan: host_nano, partial_filters=[host_nano.host = Utf8("host1"), host_nano.ts > TimestampNanosecond(-5000000000, None), host_nano.ts <= TimestampNanosecond(10000000000, None)] | -| | ]] | -| physical_plan | CooperativeExec | ++---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| plan_type | plan | ++---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| logical_plan | MergeScan [is_placeholder=false, remote_input=[ | +| | Filter: prom_avg_over_time(ts_range,val) IS NOT NULL | +| | Projection: host_nano.ts, prom_avg_over_time(ts_range, val) AS prom_avg_over_time(ts_range,val), host_nano.host | +| | PromRangeManipulate: req range=[0..10000], interval=[5000], eval range=[5000], time index=[ts], values=["val"] | +| | PromSeriesNormalize: offset=[0], time index=[ts], filter NaN: [true] | +| | PromSeriesDivide: tags=["host"] | +| | Sort: host_nano.host ASC NULLS FIRST, host_nano.ts ASC NULLS FIRST | +| | Projection: host_nano.val, host_nano.host, host_nano.ts | +| | Filter: host_nano.host = Utf8("host1") AND host_nano.ts > TimestampNanosecond(-5000000000, None) AND host_nano.ts <= TimestampNanosecond(10000000000, None) | +| | TableScan: host_nano, partial_filters=[host_nano.host = Utf8("host1"), host_nano.ts > TimestampNanosecond(-5000000000, None), host_nano.ts <= TimestampNanosecond(10000000000, None)] | +| | ]] | +| physical_plan | CooperativeExec | | | MergeScanExec: REDACTED -| | | -+---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| | | ++---------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ DROP TABLE host_sec;