mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-10-20 02:42:29 +00:00
* fix(promql): follow IEEE 754 float semantics in operators and aggregations - Comparisons: Arrow orders floats totally (NaN == NaN, NaN > +Inf); PromQL comparisons with NaN are false except !=. - min/max: skip NaN unless the whole group is NaN. DataFusion's grouped min/max let NaN replace and be replaced by neighbours, so the result depended on input order. - topk/bottomk rank NaN last. - round() rounds ties up and returns NaN for a zero step, like Prometheus. - sqrt() of a negative number is NaN instead of an error. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix(promql): round() without a step rounds to integers The planner passed a zero step for single-argument round(), which now yields NaN like Prometheus; pass 1 instead. Found by the compatibility harness. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix(promql): apply the sqrt guard to builtin functions and register min/max for plan decoding sqrt resolves through the session's builtin functions, not the UDF branch. Distributed plans are decoded with the serializer's session, which did not know prom_min/prom_max. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * test(promql): cover NaN and rounding semantics Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix(promql): keep the nested count rewrite for PromQL min/max count(min(x) by (l)) was rewritten by count_nest_aggr only for the builtin min/max names. prom_min/prom_max return NULL for the same groups, so the rewrite stays valid. Update plan snapshots for the IEEE comparison, sqrt, topk and min/max changes. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix(promql): register prom_min/prom_max in the function registry Distributed plans split an aggregation into state and merge steps only for functions whose state function exists in FUNCTION_REGISTRY; prom_min and prom_max lived in the promql crate, so min/max stopped being pushed down to datanodes. Move them to common-function and register them before the state functions are derived. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * test: update sqlness results for PromQL float semantics Plans and column names now show prom_min/prom_max, the NaN guards of comparisons and sqrt, and the NaN sort key of topk. round() results follow Prometheus' formula. Distributed min/max keep the state/merge split. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix(promql): keep column names and NULLs in IEEE comparisons and sqrt Rewriting comparisons as `a op b AND NOT (isnan(a) OR isnan(b))` changed TQL output column names and turned NULL operands into true for `!=` and false for the bool forms. Implement comparisons and sqrt as PromQL UDFs that propagate NULL and report the column name of the expression they replace; prom_min/prom_max report min/max column names the same way. Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * test: restore column names in sqlness results for PromQL float semantics Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * test(promql): share the prom_min/prom_max groups accumulator with tests Signed-off-by: Dennis Zhuang <killme2008@gmail.com> * fix(promql): keep distinct names for prom_min/prom_max Signed-off-by: Dennis Zhuang <killme2008@gmail.com> --------- Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
207 lines
9.6 KiB
Plaintext
207 lines
9.6 KiB
Plaintext
CREATE TABLE http_requests (
|
|
ts timestamp(3) time index,
|
|
host STRING,
|
|
idc STRING,
|
|
val BIGINT,
|
|
PRIMARY KEY(host, idc),
|
|
);
|
|
|
|
Affected Rows: 0
|
|
|
|
INSERT INTO TABLE http_requests VALUES
|
|
(0, 'host1', "idc1", 200),
|
|
(0, 'host2', "idc1", 200),
|
|
(0, 'host3', "idc2", 200),
|
|
(0, 'host4', "idc2", 401),
|
|
(5000, 'host1', "idc1", 404),
|
|
(5000, 'host2', "idc1", 401),
|
|
(5000, 'host3', "idc2", 404),
|
|
(5000, 'host4', "idc2", 500),
|
|
(10000, 'host1', "idc1", 200),
|
|
(10000, 'host2', "idc1", 200),
|
|
(10000, 'host3', "idc2", 201),
|
|
(10000, 'host4', "idc2", 201),
|
|
(15000, 'host1', "idc1", 500),
|
|
(15000, 'host2', "idc1", 500),
|
|
(15000, 'host3', "idc2", 500),
|
|
(15000, 'host4', "idc2", 500);
|
|
|
|
Affected Rows: 16
|
|
|
|
TQL EVAL (0, 15, '5s') count_values("status_code", http_requests);
|
|
|
|
+--------------------------+---------------------+-------------+
|
|
| count(http_requests.val) | ts | status_code |
|
|
+--------------------------+---------------------+-------------+
|
|
| 3 | 1970-01-01T00:00:00 | 200 |
|
|
| 1 | 1970-01-01T00:00:00 | 401 |
|
|
| 1 | 1970-01-01T00:00:05 | 401 |
|
|
| 2 | 1970-01-01T00:00:05 | 404 |
|
|
| 1 | 1970-01-01T00:00:05 | 500 |
|
|
| 2 | 1970-01-01T00:00:10 | 200 |
|
|
| 2 | 1970-01-01T00:00:10 | 201 |
|
|
| 4 | 1970-01-01T00:00:15 | 500 |
|
|
+--------------------------+---------------------+-------------+
|
|
|
|
TQL EVAL (0, 15, '5s') count_values("status_code", http_requests) by (idc);
|
|
|
|
+--------------------------+------+---------------------+-------------+
|
|
| count(http_requests.val) | idc | ts | status_code |
|
|
+--------------------------+------+---------------------+-------------+
|
|
| 2 | idc1 | 1970-01-01T00:00:00 | 200 |
|
|
| 1 | idc1 | 1970-01-01T00:00:05 | 401 |
|
|
| 1 | idc1 | 1970-01-01T00:00:05 | 404 |
|
|
| 2 | idc1 | 1970-01-01T00:00:10 | 200 |
|
|
| 2 | idc1 | 1970-01-01T00:00:15 | 500 |
|
|
| 1 | idc2 | 1970-01-01T00:00:00 | 200 |
|
|
| 1 | idc2 | 1970-01-01T00:00:00 | 401 |
|
|
| 1 | idc2 | 1970-01-01T00:00:05 | 404 |
|
|
| 1 | idc2 | 1970-01-01T00:00:05 | 500 |
|
|
| 2 | idc2 | 1970-01-01T00:00:10 | 201 |
|
|
| 2 | idc2 | 1970-01-01T00:00:15 | 500 |
|
|
+--------------------------+------+---------------------+-------------+
|
|
|
|
-- https://github.com/GreptimeTeam/greptimedb/issues/9181
|
|
-- The label generated by `count_values` must survive an enclosing expression,
|
|
-- e.g. a per-sample function, an arithmetic operation or an enclosing aggregation.
|
|
-- SQLNESS SORT_RESULT 3 1
|
|
TQL EVAL (0, 15, '5s') abs(count_values("v", http_requests));
|
|
|
|
+---------------------+-------------------------------+-----+
|
|
| ts | abs(count(http_requests.val)) | v |
|
|
+---------------------+-------------------------------+-----+
|
|
| 1970-01-01T00:00:00 | 1 | 401 |
|
|
| 1970-01-01T00:00:00 | 3 | 200 |
|
|
| 1970-01-01T00:00:05 | 1 | 401 |
|
|
| 1970-01-01T00:00:05 | 1 | 500 |
|
|
| 1970-01-01T00:00:05 | 2 | 404 |
|
|
| 1970-01-01T00:00:10 | 2 | 200 |
|
|
| 1970-01-01T00:00:10 | 2 | 201 |
|
|
| 1970-01-01T00:00:15 | 4 | 500 |
|
|
+---------------------+-------------------------------+-----+
|
|
|
|
-- SQLNESS SORT_RESULT 3 1
|
|
TQL EVAL (0, 15, '5s') round(count_values("v", http_requests));
|
|
|
|
+---------------------+-------------------------------------------------+-----+
|
|
| ts | prom_round(count(http_requests.val),Float64(1)) | v |
|
|
+---------------------+-------------------------------------------------+-----+
|
|
| 1970-01-01T00:00:00 | 1.0 | 401 |
|
|
| 1970-01-01T00:00:00 | 3.0 | 200 |
|
|
| 1970-01-01T00:00:05 | 1.0 | 401 |
|
|
| 1970-01-01T00:00:05 | 1.0 | 500 |
|
|
| 1970-01-01T00:00:05 | 2.0 | 404 |
|
|
| 1970-01-01T00:00:10 | 2.0 | 200 |
|
|
| 1970-01-01T00:00:10 | 2.0 | 201 |
|
|
| 1970-01-01T00:00:15 | 4.0 | 500 |
|
|
+---------------------+-------------------------------------------------+-----+
|
|
|
|
-- SQLNESS SORT_RESULT 3 1
|
|
TQL EVAL (0, 15, '5s') count_values("v", http_requests) + 1;
|
|
|
|
+-----+---------------------+---------------------------------------+
|
|
| v | ts | count(http_requests.val) + Float64(1) |
|
|
+-----+---------------------+---------------------------------------+
|
|
| 200 | 1970-01-01T00:00:00 | 4.0 |
|
|
| 200 | 1970-01-01T00:00:10 | 3.0 |
|
|
| 201 | 1970-01-01T00:00:10 | 3.0 |
|
|
| 401 | 1970-01-01T00:00:00 | 2.0 |
|
|
| 401 | 1970-01-01T00:00:05 | 2.0 |
|
|
| 404 | 1970-01-01T00:00:05 | 3.0 |
|
|
| 500 | 1970-01-01T00:00:05 | 2.0 |
|
|
| 500 | 1970-01-01T00:00:15 | 5.0 |
|
|
+-----+---------------------+---------------------------------------+
|
|
|
|
-- SQLNESS SORT_RESULT 3 1
|
|
TQL EVAL (0, 15, '5s') sum by (v) (count_values("v", http_requests));
|
|
|
|
+-----+---------------------+-------------------------------+
|
|
| v | ts | sum(count(http_requests.val)) |
|
|
+-----+---------------------+-------------------------------+
|
|
| 200 | 1970-01-01T00:00:00 | 3 |
|
|
| 200 | 1970-01-01T00:00:10 | 2 |
|
|
| 201 | 1970-01-01T00:00:10 | 2 |
|
|
| 401 | 1970-01-01T00:00:00 | 1 |
|
|
| 401 | 1970-01-01T00:00:05 | 1 |
|
|
| 404 | 1970-01-01T00:00:05 | 2 |
|
|
| 500 | 1970-01-01T00:00:05 | 1 |
|
|
| 500 | 1970-01-01T00:00:15 | 4 |
|
|
+-----+---------------------+-------------------------------+
|
|
|
|
-- SQLNESS SORT_RESULT 3 1
|
|
TQL EVAL (0, 15, '5s') topk(10, count_values("v", http_requests));
|
|
|
|
+--------------------------+-----+---------------------+
|
|
| count(http_requests.val) | v | ts |
|
|
+--------------------------+-----+---------------------+
|
|
| 1 | 401 | 1970-01-01T00:00:00 |
|
|
| 1 | 401 | 1970-01-01T00:00:05 |
|
|
| 1 | 500 | 1970-01-01T00:00:05 |
|
|
| 2 | 200 | 1970-01-01T00:00:10 |
|
|
| 2 | 201 | 1970-01-01T00:00:10 |
|
|
| 2 | 404 | 1970-01-01T00:00:05 |
|
|
| 3 | 200 | 1970-01-01T00:00:00 |
|
|
| 4 | 500 | 1970-01-01T00:00:15 |
|
|
+--------------------------+-----+---------------------+
|
|
|
|
-- The generated label overwrites an input label with the same name (PromQL semantics),
|
|
-- and must not turn the generated column into an ambiguous reference.
|
|
-- SQLNESS SORT_RESULT 3 1
|
|
TQL EVAL (0, 15, '5s') count_values("idc", http_requests) by (idc);
|
|
|
|
+--------------------------+---------------------+-----+
|
|
| count(http_requests.val) | ts | idc |
|
|
+--------------------------+---------------------+-----+
|
|
| 1 | 1970-01-01T00:00:00 | 401 |
|
|
| 1 | 1970-01-01T00:00:05 | 401 |
|
|
| 1 | 1970-01-01T00:00:05 | 500 |
|
|
| 2 | 1970-01-01T00:00:05 | 404 |
|
|
| 2 | 1970-01-01T00:00:10 | 200 |
|
|
| 2 | 1970-01-01T00:00:10 | 201 |
|
|
| 3 | 1970-01-01T00:00:00 | 200 |
|
|
| 4 | 1970-01-01T00:00:15 | 500 |
|
|
+--------------------------+---------------------+-----+
|
|
|
|
-- The generated label is the sample value in PromQL's textual form
|
|
-- (`strconv.FormatFloat(value, 'f', -1, 64)`: the shortest decimal form without an
|
|
-- exponent), so it is a string: `200.0` becomes the label `200` and `1e21` becomes
|
|
-- `1000000000000000000000`.
|
|
CREATE TABLE http_requests_double (
|
|
ts timestamp(3) time index,
|
|
host STRING,
|
|
idc STRING,
|
|
val DOUBLE,
|
|
PRIMARY KEY(host, idc),
|
|
);
|
|
|
|
Affected Rows: 0
|
|
|
|
INSERT INTO TABLE http_requests_double VALUES
|
|
(0, 'host1', "idc1", 200.0),
|
|
(0, 'host2', "idc1", 0.5),
|
|
(5000, 'host1', "idc1", 200.0),
|
|
(5000, 'host2', "idc1", 1e21);
|
|
|
|
Affected Rows: 4
|
|
|
|
-- SQLNESS SORT_RESULT 3 1
|
|
TQL EVAL (0, 5, '5s') count_values("val_label", http_requests_double);
|
|
|
|
+---------------------------------+---------------------+------------------------+
|
|
| count(http_requests_double.val) | ts | val_label |
|
|
+---------------------------------+---------------------+------------------------+
|
|
| 1 | 1970-01-01T00:00:00 | 0.5 |
|
|
| 1 | 1970-01-01T00:00:00 | 200 |
|
|
| 1 | 1970-01-01T00:00:05 | 1000000000000000000000 |
|
|
| 1 | 1970-01-01T00:00:05 | 200 |
|
|
+---------------------------------+---------------------+------------------------+
|
|
|
|
DROP TABLE http_requests_double;
|
|
|
|
Affected Rows: 0
|
|
|
|
DROP TABLE http_requests;
|
|
|
|
Affected Rows: 0
|
|
|