Files
greptimedb/tests/cases/standalone/common/function/json/json.sql
T
dennis zhuang 4dd92c774e feat: add json_object function and use it in the entity-graph derivation (#8870)
* feat: add json_object scalar function

Builds a JSONB object from interleaved (key, value, ...) arguments, like
MySQL's JSON_OBJECT. Values are written into the binary directly, so
JSON-hostile characters (quotes, backslashes, control characters) need no
text-level escaping. Keys must be non-NULL strings; values may be strings,
numbers, booleans, or NULL (JSON null).

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix: build entity-graph JSON objects with json_object

The derivation assembled entity_id_attrs and descriptive by concatenating a
JSON text and parsing it, escaping only backslash and double quote in runtime
values. A label containing a control character (e.g. a newline) produced
unparseable text and failed the whole semantic_entities scan instead of one
attribute. json_object assembles the JSONB binary directly from the value
columns, so no text escaping is involved; NULL-to-'' stays at the call site.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* chore: trim comments and fold duplicate test coverage

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* fix: json_object() returns an empty object; narrow values to integers and floats

MySQL's JSON_OBJECT allows an empty pair list, so the signature accepts zero
arguments and the row count falls back to number_rows. Decimals stay rejected
instead of casting to Float64: JSONB numbers (i64/u64/f64) cannot represent
them exactly and a silent precision loss is worse than an explicit cast.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

* chore: document key-to-string conversion and align test naming

Keys follow MySQL JSON_OBJECT: any castable type is converted to string.
Rustdoc and the cast-failure message now say so, with a numeric-key test.
Test names take the module-conventional test_ prefix.

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>

---------

Signed-off-by: Dennis Zhuang <killme2008@gmail.com>
2026-08-14 08:24:50 +00:00

65 lines
1.8 KiB
SQL

--- json_object_keys ---
SELECT json_object_keys(parse_json('{"a": 1, "b": {"c": 2}}'));
SELECT json_object_keys(parse_json('{}'));
SELECT json_object_keys(parse_json('[1, 2]'));
SELECT json_object_keys(parse_json('null'));
SELECT json_object_keys(NULL);
--- json_path_exists ---
SELECT json_path_exists(parse_json('{"a": 1, "b": 2}'), '$.a');
SELECT json_path_exists(parse_json('{"a": 1, "b": 2}'), '$.c');
SELECT json_path_exists(parse_json('[1, 2]'), '[0]');
SELECT json_path_exists(parse_json('[1, 2]'), '[2]');
SELECT json_path_exists(parse_json('[1, 2]'), 'null');
SELECT json_path_exists(parse_json('null'), '$.a');
SELECT json_path_exists(NULL, '$.a');
SELECT json_path_exists(parse_json('{}'), NULL);
--- json_path_match ---
SELECT json_path_match(parse_json('{"a": 1, "b": 2}'), '$.a == 1');
SELECT json_path_match(parse_json('{"a":1,"b":[1,2,3]}'), '$.b[0] > 1');
SELECT json_path_match(parse_json('{"a":1,"b":[1,2,3]}'), '$.b[1 to last] >= 2');
SELECT json_path_match(parse_json('{"a":1,"b":[1,2,3]}'), 'null');
SELECT json_path_match(parse_json('null'), '$.a == 1');
--- json_object ---
SELECT json_to_string(json_object('a', 1, 'b', 'text', 'c', true, 'd', 1.5));
SELECT json_to_string(json_object());
SELECT json_to_string(json_object('nul', NULL));
SELECT json_to_string(json_object('nl', concat('line1', chr(10), 'line2'), 'q', 'quote"back\slash'));
SELECT json_object('a');
SELECT json_object(NULL, 1);
SELECT json_object('ts', to_timestamp(0));
SELECT json_object('price', CAST('12.34' AS DECIMAL(10, 2)));
CREATE TABLE json_object_src (ts TIMESTAMP TIME INDEX, host STRING, pid BIGINT);
INSERT INTO json_object_src VALUES (0, 'h1', 42), (1, NULL, 7);
SELECT json_to_string(json_object('host', host, 'pid', pid)) FROM json_object_src ORDER BY ts;
DROP TABLE json_object_src;