From 12b2cc5ad3ba561d669a60f93b79f2a33ca05cd8 Mon Sep 17 00:00:00 2001 From: discord9 <55937128+discord9@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:25:21 +0800 Subject: [PATCH] test(promql): exercise LastRow hints and filtered results Signed-off-by: discord9 <55937128+discord9@users.noreply.github.com> --- .../common/promql/instant_last_row.result | 63 +++++++++++++++++++ .../common/promql/instant_last_row.sql | 32 ++++++++++ .../promql_instant_last_row_9034/case.toml | 6 +- 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/tests/cases/standalone/common/promql/instant_last_row.result b/tests/cases/standalone/common/promql/instant_last_row.result index 8b81c01d8b4..5093a357ea8 100644 --- a/tests/cases/standalone/common/promql/instant_last_row.result +++ b/tests/cases/standalone/common/promql/instant_last_row.result @@ -408,6 +408,69 @@ DROP TABLE instant_last_nano; Affected Rows: 0 +-- A field selector chooses the val column; it does not filter val. PromQL must +-- apply the comparison after selecting the latest eligible sample, rather than +-- falling back to an older sample that satisfies the comparison. +CREATE TABLE instant_last_field_filter ( + ts TIMESTAMP TIME INDEX, + val DOUBLE, + host STRING, + instance STRING, + PRIMARY KEY (host, instance) +) ENGINE=mito; + +Affected Rows: 0 + +-- Keep the older matching sample in an SST and the latest non-matching sample +-- in the memtable. Both timestamps are eligible at 1s. +INSERT INTO instant_last_field_filter VALUES (900, 10, 'host-a', 'instance-a'); + +Affected Rows: 1 + +ADMIN FLUSH_TABLE('instant_last_field_filter'); + ++------------------------------------------------+ +| ADMIN FLUSH_TABLE('instant_last_field_filter') | ++------------------------------------------------+ +| 0 | ++------------------------------------------------+ + +INSERT INTO instant_last_field_filter VALUES (1000, 1, 'host-a', 'instance-a'); + +Affected Rows: 1 + +-- Expected: no rows. The selected latest value is 1, so it must not fall back +-- to the older value 10 merely because that value is greater than 5. +-- SQLNESS SORT_RESULT 3 1 +TQL EVAL (1, 1, '1s') instant_last_field_filter{__field__="val"} > 5; + +++ +++ + +-- A value matcher filters the scan before selecting a sample: the older 10 +-- remains eligible, unlike the post-selection comparison above. +TQL EVAL (1, 1, '1s') instant_last_field_filter{val="10.0"}; + ++---------------------+------+--------+------------+ +| ts | val | host | instance | ++---------------------+------+--------+------------+ +| 1970-01-01T00:00:01 | 10.0 | host-a | instance-a | ++---------------------+------+--------+------------+ + +-- SQL filters rows before aggregation, so the older matching value remains. +-- Expected value: 10. +SELECT last_value(val ORDER BY ts) FROM instant_last_field_filter WHERE val > 5; + ++--------------------------------------------------------------------------------------------------+ +| last_value(instant_last_field_filter.val) ORDER BY [instant_last_field_filter.ts ASC NULLS LAST] | ++--------------------------------------------------------------------------------------------------+ +| 10.0 | ++--------------------------------------------------------------------------------------------------+ + +DROP TABLE instant_last_field_filter; + +Affected Rows: 0 + -- Range evaluation is a control: range functions need their complete windows, -- not a single last row. At 10s and 20s, [11s] contains two samples. CREATE TABLE instant_last_range_control ( diff --git a/tests/cases/standalone/common/promql/instant_last_row.sql b/tests/cases/standalone/common/promql/instant_last_row.sql index 6c0bb1c8717..9df036f2eea 100644 --- a/tests/cases/standalone/common/promql/instant_last_row.sql +++ b/tests/cases/standalone/common/promql/instant_last_row.sql @@ -192,6 +192,38 @@ DROP TABLE instant_last_sec; DROP TABLE instant_last_micro; DROP TABLE instant_last_nano; +-- A field selector chooses the val column; it does not filter val. PromQL must +-- apply the comparison after selecting the latest eligible sample, rather than +-- falling back to an older sample that satisfies the comparison. +CREATE TABLE instant_last_field_filter ( + ts TIMESTAMP TIME INDEX, + val DOUBLE, + host STRING, + instance STRING, + PRIMARY KEY (host, instance) +) ENGINE=mito; + +-- Keep the older matching sample in an SST and the latest non-matching sample +-- in the memtable. Both timestamps are eligible at 1s. +INSERT INTO instant_last_field_filter VALUES (900, 10, 'host-a', 'instance-a'); +ADMIN FLUSH_TABLE('instant_last_field_filter'); +INSERT INTO instant_last_field_filter VALUES (1000, 1, 'host-a', 'instance-a'); + +-- Expected: no rows. The selected latest value is 1, so it must not fall back +-- to the older value 10 merely because that value is greater than 5. +-- SQLNESS SORT_RESULT 3 1 +TQL EVAL (1, 1, '1s') instant_last_field_filter{__field__="val"} > 5; + +-- A value matcher filters the scan before selecting a sample: the older 10 +-- remains eligible, unlike the post-selection comparison above. +TQL EVAL (1, 1, '1s') instant_last_field_filter{val="10.0"}; + +-- SQL filters rows before aggregation, so the older matching value remains. +-- Expected value: 10. +SELECT last_value(val ORDER BY ts) FROM instant_last_field_filter WHERE val > 5; + +DROP TABLE instant_last_field_filter; + -- Range evaluation is a control: range functions need their complete windows, -- not a single last row. At 10s and 20s, [11s] contains two samples. CREATE TABLE instant_last_range_control ( diff --git a/tests/perf/query_cases/promql_instant_last_row_9034/case.toml b/tests/perf/query_cases/promql_instant_last_row_9034/case.toml index 6dd5dc2fb8b..6e61a094b32 100644 --- a/tests/perf/query_cases/promql_instant_last_row_9034/case.toml +++ b/tests/perf/query_cases/promql_instant_last_row_9034/case.toml @@ -1,7 +1,7 @@ # PromQL instant-selector latency regression coverage for #9034. # # Instant, range, and aggregate controls cover LastRow latency behavior. The -# candidate instant query may enable the existing LastRow selector; the range +# candidate instant query must exercise the existing LastRow selector; the range # query must not. This is latency regression coverage only: optimizer and # sqlness tests own plan and correctness coverage. Evaluation is 0.7s after the # final generated sample (last timestamp = start + 1023 * 0.1s = @@ -44,7 +44,7 @@ distribution = { kind = "deterministic_wave", min = 0.0, max = 1000.0 } [[scenario.tables.columns]] name = "ts" -type = "TIMESTAMP(9)" +type = "TIMESTAMP(3)" semantic = "timestamp" [scenario.layout] @@ -58,7 +58,7 @@ step_nanos = 100000000 time_range_layout = "non_overlapping_per_sst" series_layout = "timestamp_major" -# Candidate instant query may enable the existing LastRow selector. +# Candidate instant query must exercise the existing LastRow selector. [[scenario.queries]] name = "instant_selector_last_row_candidate" kind = "tql"