From af49e4e5e17c81232502cd88b42229134969e81b Mon Sep 17 00:00:00 2001 From: fys Date: Wed, 26 Aug 2026 15:17:36 +0800 Subject: [PATCH] fix: support empty objects in JSON2 Signed-off-by: fys --- src/datatypes/src/json.rs | 18 + src/datatypes/src/vectors/json/array.rs | 14 + src/datatypes/src/vectors/json/builder.rs | 21 +- .../common/types/json/json2_limit.result | 4 - .../common/types/json/json2_limit.sql | 2 - .../common/types/json/json_empty.result | 510 ++++++++++++++++++ .../common/types/json/json_empty.sql | 239 ++++++++ 7 files changed, 801 insertions(+), 7 deletions(-) create mode 100644 tests/cases/standalone/common/types/json/json_empty.result create mode 100644 tests/cases/standalone/common/types/json/json_empty.sql diff --git a/src/datatypes/src/json.rs b/src/datatypes/src/json.rs index ec9ca4ac789..60e42ae69f6 100644 --- a/src/datatypes/src/json.rs +++ b/src/datatypes/src/json.rs @@ -140,6 +140,13 @@ impl JsonSettings { /// Encode a serde_json::Value into a Value::Json using current settings. pub fn encode(&self, json: Json) -> Result { + if self.type_hints.is_empty() + && self.max_auto_expanded_paths.is_none() + && let Json::Object(object) = &json + && object.is_empty() + { + return Ok(Value::Json(Box::new(JsonValue::null()))); + } let mut context = JsonContext { path: Vec::new(), settings: self, @@ -777,6 +784,17 @@ mod tests { } } + #[test] + fn test_encode_root_empty_object_without_type_hints_as_json_null() -> Result<()> { + let settings = JsonSettings::default(); + + assert_eq!( + Value::Json(Box::new(JsonValue::null())), + settings.encode(json!({}))? + ); + Ok(()) + } + #[test] fn test_encode_json_object() { let json = json!({ diff --git a/src/datatypes/src/vectors/json/array.rs b/src/datatypes/src/vectors/json/array.rs index 0426334c3dd..33c26b3903e 100644 --- a/src/datatypes/src/vectors/json/array.rs +++ b/src/datatypes/src/vectors/json/array.rs @@ -180,6 +180,10 @@ impl JsonArray<'_> { data_type, expect ); + if data_type == &DataType::Null { + return Ok(new_null_array(expect, self.inner.len())); + } + let struct_array = self.inner.as_struct_opt().context(AlignJsonArraySnafu { reason: "expect struct array", })?; @@ -709,6 +713,16 @@ mod test { assert_eq!(2, widened.null_count()); } + let target_type = DataType::Struct(Fields::from(vec![Field::new( + "value", + DataType::Int64, + true, + )])); + let widened = JsonArray::from(&nulls).widen_to(&target_type)?; + assert_eq!(&target_type, widened.data_type()); + assert_eq!(2, widened.len()); + assert_eq!(2, widened.null_count()); + Ok(()) } diff --git a/src/datatypes/src/vectors/json/builder.rs b/src/datatypes/src/vectors/json/builder.rs index e6ceb6d6a5f..53bfbb2bbe6 100644 --- a/src/datatypes/src/vectors/json/builder.rs +++ b/src/datatypes/src/vectors/json/builder.rs @@ -24,7 +24,7 @@ use crate::prelude::{ValueRef, Vector, VectorRef}; use crate::types::StructType; use crate::types::json_type::{JsonNativeType, is_include}; use crate::value::{ListValue, StructValue, StructValueRef, Value}; -use crate::vectors::{MutableVector, StructVectorBuilder}; +use crate::vectors::{MutableVector, NullVector, StructVectorBuilder}; #[derive(Clone)] pub(crate) struct JsonVectorBuilder { @@ -45,6 +45,12 @@ impl JsonVectorBuilder { } fn try_build(&mut self) -> Result { + if matches!(self.merged_type, JsonNativeType::Null) { + let len = self.values.len(); + self.values.clear(); + return Ok(Arc::new(NullVector::new(len))); + } + let DataType::Struct(fields) = self.merged_type.as_arrow_type() else { return UnexpectedSnafu { reason: "merged JSON2 type must map to Arrow Struct in JsonVectorBuilder", @@ -357,6 +363,19 @@ mod tests { .unwrap_err(); assert!(err.to_string().contains("expected json value")); + let mut null_builder = JsonVectorBuilder::new(JsonNativeType::Null, 2); + null_builder.try_push_value_ref(&ValueRef::Json(Box::new( + crate::json::value::JsonValueRef::null(), + )))?; + null_builder.try_push_value_ref(&ValueRef::Json(Box::new( + crate::json::value::JsonValueRef::null(), + )))?; + + let vector = null_builder.to_vector(); + assert_eq!(2, vector.len()); + assert_eq!(Value::Null, vector.get(0)); + assert_eq!(Value::Null, vector.get(1)); + Ok(()) } diff --git a/tests/cases/standalone/common/types/json/json2_limit.result b/tests/cases/standalone/common/types/json/json2_limit.result index 035c4ed07bf..461ec8058c5 100644 --- a/tests/cases/standalone/common/types/json/json2_limit.result +++ b/tests/cases/standalone/common/types/json/json2_limit.result @@ -28,10 +28,6 @@ insert into json2_disable_non_object_insert values (5, 'null'); Error: 1001(Unsupported), Non-object json is not supported currently -insert into json2_disable_non_object_insert values (6, '{}'); - -Error: 1004(InvalidArguments), Invalid InsertRequest, reason: empty json object is not supported, consider adding a dummy field - drop table json2_disable_non_object_insert; Affected Rows: 0 diff --git a/tests/cases/standalone/common/types/json/json2_limit.sql b/tests/cases/standalone/common/types/json/json2_limit.sql index 986ed58eb2a..e68eb7c5222 100644 --- a/tests/cases/standalone/common/types/json/json2_limit.sql +++ b/tests/cases/standalone/common/types/json/json2_limit.sql @@ -16,8 +16,6 @@ insert into json2_disable_non_object_insert values (4, 'true'); insert into json2_disable_non_object_insert values (5, 'null'); -insert into json2_disable_non_object_insert values (6, '{}'); - drop table json2_disable_non_object_insert; create table json2_whole_and_path_read ( diff --git a/tests/cases/standalone/common/types/json/json_empty.result b/tests/cases/standalone/common/types/json/json_empty.result new file mode 100644 index 00000000000..6cddc2bdb2a --- /dev/null +++ b/tests/cases/standalone/common/types/json/json_empty.result @@ -0,0 +1,510 @@ +CREATE TABLE json_empty_no_type_hints ( + ts TIMESTAMP TIME INDEX, + app_name STRING, + log_level STRING, + log_message STRING, + attrs JSON2 +) WITH ( + 'append_mode' = 'true' +); + +Affected Rows: 0 + +INSERT INTO json_empty_no_type_hints +VALUES + (1, 'checkout', 'INFO', 'request completed', '{}'); + +Affected Rows: 1 + +SELECT * +FROM json_empty_no_type_hints +ORDER BY ts; + ++-------------------------+----------+-----------+-------------------+-------+ +| ts | app_name | log_level | log_message | attrs | ++-------------------------+----------+-----------+-------------------+-------+ +| 1970-01-01T00:00:00.001 | checkout | INFO | request completed | | ++-------------------------+----------+-----------+-------------------+-------+ + +INSERT INTO json_empty_no_type_hints +VALUES + (2, 'checkout', 'INFO', 'request completed', '{"a": 1}'); + +Affected Rows: 1 + +SELECT + ts, + app_name, + log_level, + log_message, + json_get(attrs, 'a')::int64 +FROM json_empty_no_type_hints +ORDER BY ts; + ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ +| ts | app_name | log_level | log_message | arrow_cast(json_get(json_empty_no_type_hints.attrs,Utf8("a")),Utf8("Int64")) | ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ +| 1970-01-01T00:00:00.001 | checkout | INFO | request completed | | +| 1970-01-01T00:00:00.002 | checkout | INFO | request completed | 1 | ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ + +SELECT * +FROM json_empty_no_type_hints +ORDER BY ts; + ++-------------------------+----------+-----------+-------------------+---------+ +| ts | app_name | log_level | log_message | attrs | ++-------------------------+----------+-----------+-------------------+---------+ +| 1970-01-01T00:00:00.001 | checkout | INFO | request completed | | +| 1970-01-01T00:00:00.002 | checkout | INFO | request completed | {"a":1} | ++-------------------------+----------+-----------+-------------------+---------+ + +ADMIN FLUSH_TABLE('json_empty_no_type_hints'); + ++-----------------------------------------------+ +| ADMIN FLUSH_TABLE('json_empty_no_type_hints') | ++-----------------------------------------------+ +| 0 | ++-----------------------------------------------+ + +SELECT + ts, + app_name, + log_level, + log_message, + json_get(attrs, 'a')::int64 +FROM json_empty_no_type_hints +ORDER BY ts; + ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ +| ts | app_name | log_level | log_message | arrow_cast(json_get(json_empty_no_type_hints.attrs,Utf8("a")),Utf8("Int64")) | ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ +| 1970-01-01T00:00:00.001 | checkout | INFO | request completed | | +| 1970-01-01T00:00:00.002 | checkout | INFO | request completed | 1 | ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ + +INSERT INTO json_empty_no_type_hints +VALUES + (3, 'checkout', 'INFO', 'request completed', '{"a": 2}'); + +Affected Rows: 1 + +SELECT + ts, + app_name, + log_level, + log_message, + json_get(attrs, 'a')::int64 +FROM json_empty_no_type_hints +ORDER BY ts; + ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ +| ts | app_name | log_level | log_message | arrow_cast(json_get(json_empty_no_type_hints.attrs,Utf8("a")),Utf8("Int64")) | ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ +| 1970-01-01T00:00:00.001 | checkout | INFO | request completed | | +| 1970-01-01T00:00:00.002 | checkout | INFO | request completed | 1 | +| 1970-01-01T00:00:00.003 | checkout | INFO | request completed | 2 | ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ + +ADMIN FLUSH_TABLE('json_empty_no_type_hints'); + ++-----------------------------------------------+ +| ADMIN FLUSH_TABLE('json_empty_no_type_hints') | ++-----------------------------------------------+ +| 0 | ++-----------------------------------------------+ + +ADMIN COMPACT_TABLE('json_empty_no_type_hints', 'swcs', '86400'); + ++------------------------------------------------------------------+ +| ADMIN COMPACT_TABLE('json_empty_no_type_hints', 'swcs', '86400') | ++------------------------------------------------------------------+ +| 0 | ++------------------------------------------------------------------+ + +SELECT + ts, + app_name, + log_level, + log_message, + json_get(attrs, 'a')::int64 +FROM json_empty_no_type_hints +ORDER BY ts; + ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ +| ts | app_name | log_level | log_message | arrow_cast(json_get(json_empty_no_type_hints.attrs,Utf8("a")),Utf8("Int64")) | ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ +| 1970-01-01T00:00:00.001 | checkout | INFO | request completed | | +| 1970-01-01T00:00:00.002 | checkout | INFO | request completed | 1 | +| 1970-01-01T00:00:00.003 | checkout | INFO | request completed | 2 | ++-------------------------+----------+-----------+-------------------+------------------------------------------------------------------------------+ + +CREATE TABLE json_empty_after_object ( + ts TIMESTAMP TIME INDEX, + attrs JSON2 +) WITH ( + 'append_mode' = 'true' +); + +Affected Rows: 0 + +INSERT INTO json_empty_after_object +VALUES + (1, '{"a": 1}'), + (2, '{}'); + +Affected Rows: 2 + +SELECT ts, json_get(attrs, 'a')::int64 +FROM json_empty_after_object +ORDER BY ts; + ++-------------------------+-----------------------------------------------------------------------------+ +| ts | arrow_cast(json_get(json_empty_after_object.attrs,Utf8("a")),Utf8("Int64")) | ++-------------------------+-----------------------------------------------------------------------------+ +| 1970-01-01T00:00:00.001 | 1 | +| 1970-01-01T00:00:00.002 | | ++-------------------------+-----------------------------------------------------------------------------+ + +SELECT * +FROM json_empty_after_object +ORDER BY ts; + ++-------------------------+---------+ +| ts | attrs | ++-------------------------+---------+ +| 1970-01-01T00:00:00.001 | {"a":1} | +| 1970-01-01T00:00:00.002 | | ++-------------------------+---------+ + +ADMIN FLUSH_TABLE('json_empty_after_object'); + ++----------------------------------------------+ +| ADMIN FLUSH_TABLE('json_empty_after_object') | ++----------------------------------------------+ +| 0 | ++----------------------------------------------+ + +SELECT ts, json_get(attrs, 'a')::int64 +FROM json_empty_after_object +ORDER BY ts; + ++-------------------------+-----------------------------------------------------------------------------+ +| ts | arrow_cast(json_get(json_empty_after_object.attrs,Utf8("a")),Utf8("Int64")) | ++-------------------------+-----------------------------------------------------------------------------+ +| 1970-01-01T00:00:00.001 | 1 | +| 1970-01-01T00:00:00.002 | | ++-------------------------+-----------------------------------------------------------------------------+ + +INSERT INTO json_empty_after_object +VALUES (3, '{}'); + +Affected Rows: 1 + +ADMIN FLUSH_TABLE('json_empty_after_object'); + ++----------------------------------------------+ +| ADMIN FLUSH_TABLE('json_empty_after_object') | ++----------------------------------------------+ +| 0 | ++----------------------------------------------+ + +ADMIN COMPACT_TABLE('json_empty_after_object', 'swcs', '86400'); + ++-----------------------------------------------------------------+ +| ADMIN COMPACT_TABLE('json_empty_after_object', 'swcs', '86400') | ++-----------------------------------------------------------------+ +| 0 | ++-----------------------------------------------------------------+ + +SELECT ts, json_get(attrs, 'a')::int64 +FROM json_empty_after_object +ORDER BY ts; + ++-------------------------+-----------------------------------------------------------------------------+ +| ts | arrow_cast(json_get(json_empty_after_object.attrs,Utf8("a")),Utf8("Int64")) | ++-------------------------+-----------------------------------------------------------------------------+ +| 1970-01-01T00:00:00.001 | 1 | +| 1970-01-01T00:00:00.002 | | +| 1970-01-01T00:00:00.003 | | ++-------------------------+-----------------------------------------------------------------------------+ + +SELECT * +FROM json_empty_after_object +ORDER BY ts; + ++-------------------------+---------+ +| ts | attrs | ++-------------------------+---------+ +| 1970-01-01T00:00:00.001 | {"a":1} | +| 1970-01-01T00:00:00.002 | | +| 1970-01-01T00:00:00.003 | | ++-------------------------+---------+ + +CREATE TABLE json_empty_only_sst ( + ts TIMESTAMP TIME INDEX, + attrs JSON2 +) WITH ( + 'append_mode' = 'true' +); + +Affected Rows: 0 + +INSERT INTO json_empty_only_sst +VALUES (1, '{}'); + +Affected Rows: 1 + +ADMIN FLUSH_TABLE('json_empty_only_sst'); + ++------------------------------------------+ +| ADMIN FLUSH_TABLE('json_empty_only_sst') | ++------------------------------------------+ +| 0 | ++------------------------------------------+ + +SELECT * +FROM json_empty_only_sst +ORDER BY ts; + ++-------------------------+-------+ +| ts | attrs | ++-------------------------+-------+ +| 1970-01-01T00:00:00.001 | | ++-------------------------+-------+ + +ADMIN COMPACT_TABLE('json_empty_only_sst', 'swcs', '86400'); + ++-------------------------------------------------------------+ +| ADMIN COMPACT_TABLE('json_empty_only_sst', 'swcs', '86400') | ++-------------------------------------------------------------+ +| 0 | ++-------------------------------------------------------------+ + +SELECT * +FROM json_empty_only_sst +ORDER BY ts; + ++-------------------------+-------+ +| ts | attrs | ++-------------------------+-------+ +| 1970-01-01T00:00:00.001 | | ++-------------------------+-------+ + +CREATE TABLE json_empty_conflict ( + ts TIMESTAMP TIME INDEX, + attrs JSON2 +) WITH ( + 'append_mode' = 'true' +); + +Affected Rows: 0 + +INSERT INTO json_empty_conflict +VALUES + (1, '{}'), + (2, '{"a": 1}'); + +Affected Rows: 2 + +ADMIN FLUSH_TABLE('json_empty_conflict'); + ++------------------------------------------+ +| ADMIN FLUSH_TABLE('json_empty_conflict') | ++------------------------------------------+ +| 0 | ++------------------------------------------+ + +INSERT INTO json_empty_conflict +VALUES (3, '{"a": "x"}'); + +Affected Rows: 1 + +SELECT ts, json_get(attrs, 'a') +FROM json_empty_conflict +ORDER BY ts; + ++-------------------------+-----------------------------------------------+ +| ts | json_get(json_empty_conflict.attrs,Utf8("a")) | ++-------------------------+-----------------------------------------------+ +| 1970-01-01T00:00:00.001 | | +| 1970-01-01T00:00:00.002 | 1 | +| 1970-01-01T00:00:00.003 | x | ++-------------------------+-----------------------------------------------+ + +SELECT * +FROM json_empty_conflict +ORDER BY ts; + ++-------------------------+-----------+ +| ts | attrs | ++-------------------------+-----------+ +| 1970-01-01T00:00:00.001 | | +| 1970-01-01T00:00:00.002 | {"a":1} | +| 1970-01-01T00:00:00.003 | {"a":"x"} | ++-------------------------+-----------+ + +ADMIN FLUSH_TABLE('json_empty_conflict'); + ++------------------------------------------+ +| ADMIN FLUSH_TABLE('json_empty_conflict') | ++------------------------------------------+ +| 0 | ++------------------------------------------+ + +ADMIN COMPACT_TABLE('json_empty_conflict', 'swcs', '86400'); + ++-------------------------------------------------------------+ +| ADMIN COMPACT_TABLE('json_empty_conflict', 'swcs', '86400') | ++-------------------------------------------------------------+ +| 0 | ++-------------------------------------------------------------+ + +SELECT ts, json_get(attrs, 'a') +FROM json_empty_conflict +ORDER BY ts; + ++-------------------------+-----------------------------------------------+ +| ts | json_get(json_empty_conflict.attrs,Utf8("a")) | ++-------------------------+-----------------------------------------------+ +| 1970-01-01T00:00:00.001 | | +| 1970-01-01T00:00:00.002 | 1 | +| 1970-01-01T00:00:00.003 | x | ++-------------------------+-----------------------------------------------+ + +SELECT * +FROM json_empty_conflict +ORDER BY ts; + ++-------------------------+-----------+ +| ts | attrs | ++-------------------------+-----------+ +| 1970-01-01T00:00:00.001 | | +| 1970-01-01T00:00:00.002 | {"a":1} | +| 1970-01-01T00:00:00.003 | {"a":"x"} | ++-------------------------+-----------+ + +CREATE TABLE json_empty_type_hints_nullable ( + ts TIMESTAMP TIME INDEX, + j JSON2 ( + user.age BIGINT NULL + ) +) WITH ( + 'append_mode' = 'true' +); + +Affected Rows: 0 + +INSERT INTO json_empty_type_hints_nullable +VALUES (1, '{}'); + +Affected Rows: 1 + +ADMIN FLUSH_TABLE('json_empty_type_hints_nullable'); + ++-----------------------------------------------------+ +| ADMIN FLUSH_TABLE('json_empty_type_hints_nullable') | ++-----------------------------------------------------+ +| 0 | ++-----------------------------------------------------+ + +INSERT INTO json_empty_type_hints_nullable +VALUES (2, '{}'); + +Affected Rows: 1 + +SELECT j.user.age +FROM json_empty_type_hints_nullable +ORDER BY ts; + ++-------------------------------------------------------------+ +| json_get(json_empty_type_hints_nullable.j,Utf8("user.age")) | ++-------------------------------------------------------------+ +| | +| | ++-------------------------------------------------------------+ + +CREATE TABLE json_empty_type_hints_default ( + ts TIMESTAMP TIME INDEX, + j JSON2 ( + user.age BIGINT NULL DEFAULT 18 + ) +) WITH ( + 'append_mode' = 'true' +); + +Affected Rows: 0 + +INSERT INTO json_empty_type_hints_default +VALUES (1, '{}'); + +Affected Rows: 1 + +ADMIN FLUSH_TABLE('json_empty_type_hints_default'); + ++----------------------------------------------------+ +| ADMIN FLUSH_TABLE('json_empty_type_hints_default') | ++----------------------------------------------------+ +| 0 | ++----------------------------------------------------+ + +INSERT INTO json_empty_type_hints_default +VALUES (2, '{}'); + +Affected Rows: 1 + +SELECT j.user.age +FROM json_empty_type_hints_default +ORDER BY ts; + ++------------------------------------------------------------+ +| json_get(json_empty_type_hints_default.j,Utf8("user.age")) | ++------------------------------------------------------------+ +| 18 | +| 18 | ++------------------------------------------------------------+ + +CREATE TABLE json_empty_type_hints_required ( + ts TIMESTAMP TIME INDEX, + j JSON2 ( + user.age BIGINT NOT NULL + ) +) WITH ( + 'append_mode' = 'true' +); + +Affected Rows: 0 + +INSERT INTO json_empty_type_hints_required +VALUES (1, '{}'); + +Error: 1004(InvalidArguments), Invalid JSON: missing non-null JSON2 type hint path user.age + +DROP TABLE json_empty_no_type_hints; + +Affected Rows: 0 + +DROP TABLE json_empty_after_object; + +Affected Rows: 0 + +DROP TABLE json_empty_only_sst; + +Affected Rows: 0 + +DROP TABLE json_empty_conflict; + +Affected Rows: 0 + +DROP TABLE json_empty_type_hints_nullable; + +Affected Rows: 0 + +DROP TABLE json_empty_type_hints_default; + +Affected Rows: 0 + +DROP TABLE json_empty_type_hints_required; + +Affected Rows: 0 + diff --git a/tests/cases/standalone/common/types/json/json_empty.sql b/tests/cases/standalone/common/types/json/json_empty.sql new file mode 100644 index 00000000000..30ddc6b3474 --- /dev/null +++ b/tests/cases/standalone/common/types/json/json_empty.sql @@ -0,0 +1,239 @@ +CREATE TABLE json_empty_no_type_hints ( + ts TIMESTAMP TIME INDEX, + app_name STRING, + log_level STRING, + log_message STRING, + attrs JSON2 +) WITH ( + 'append_mode' = 'true' +); + +INSERT INTO json_empty_no_type_hints +VALUES + (1, 'checkout', 'INFO', 'request completed', '{}'); + +SELECT * +FROM json_empty_no_type_hints +ORDER BY ts; + +INSERT INTO json_empty_no_type_hints +VALUES + (2, 'checkout', 'INFO', 'request completed', '{"a": 1}'); + +SELECT + ts, + app_name, + log_level, + log_message, + json_get(attrs, 'a')::int64 +FROM json_empty_no_type_hints +ORDER BY ts; + +SELECT * +FROM json_empty_no_type_hints +ORDER BY ts; + +ADMIN FLUSH_TABLE('json_empty_no_type_hints'); + +SELECT + ts, + app_name, + log_level, + log_message, + json_get(attrs, 'a')::int64 +FROM json_empty_no_type_hints +ORDER BY ts; + +INSERT INTO json_empty_no_type_hints +VALUES + (3, 'checkout', 'INFO', 'request completed', '{"a": 2}'); + +SELECT + ts, + app_name, + log_level, + log_message, + json_get(attrs, 'a')::int64 +FROM json_empty_no_type_hints +ORDER BY ts; + +ADMIN FLUSH_TABLE('json_empty_no_type_hints'); + +ADMIN COMPACT_TABLE('json_empty_no_type_hints', 'swcs', '86400'); + +SELECT + ts, + app_name, + log_level, + log_message, + json_get(attrs, 'a')::int64 +FROM json_empty_no_type_hints +ORDER BY ts; + +CREATE TABLE json_empty_after_object ( + ts TIMESTAMP TIME INDEX, + attrs JSON2 +) WITH ( + 'append_mode' = 'true' +); + +INSERT INTO json_empty_after_object +VALUES + (1, '{"a": 1}'), + (2, '{}'); + +SELECT ts, json_get(attrs, 'a')::int64 +FROM json_empty_after_object +ORDER BY ts; + +SELECT * +FROM json_empty_after_object +ORDER BY ts; + +ADMIN FLUSH_TABLE('json_empty_after_object'); + +SELECT ts, json_get(attrs, 'a')::int64 +FROM json_empty_after_object +ORDER BY ts; + +INSERT INTO json_empty_after_object +VALUES (3, '{}'); + +ADMIN FLUSH_TABLE('json_empty_after_object'); + +ADMIN COMPACT_TABLE('json_empty_after_object', 'swcs', '86400'); + +SELECT ts, json_get(attrs, 'a')::int64 +FROM json_empty_after_object +ORDER BY ts; + +SELECT * +FROM json_empty_after_object +ORDER BY ts; + +CREATE TABLE json_empty_only_sst ( + ts TIMESTAMP TIME INDEX, + attrs JSON2 +) WITH ( + 'append_mode' = 'true' +); + +INSERT INTO json_empty_only_sst +VALUES (1, '{}'); + +ADMIN FLUSH_TABLE('json_empty_only_sst'); + +SELECT * +FROM json_empty_only_sst +ORDER BY ts; + +ADMIN COMPACT_TABLE('json_empty_only_sst', 'swcs', '86400'); + +SELECT * +FROM json_empty_only_sst +ORDER BY ts; + +CREATE TABLE json_empty_conflict ( + ts TIMESTAMP TIME INDEX, + attrs JSON2 +) WITH ( + 'append_mode' = 'true' +); + +INSERT INTO json_empty_conflict +VALUES + (1, '{}'), + (2, '{"a": 1}'); + +ADMIN FLUSH_TABLE('json_empty_conflict'); + +INSERT INTO json_empty_conflict +VALUES (3, '{"a": "x"}'); + +SELECT ts, json_get(attrs, 'a') +FROM json_empty_conflict +ORDER BY ts; + +SELECT * +FROM json_empty_conflict +ORDER BY ts; + +ADMIN FLUSH_TABLE('json_empty_conflict'); + +ADMIN COMPACT_TABLE('json_empty_conflict', 'swcs', '86400'); + +SELECT ts, json_get(attrs, 'a') +FROM json_empty_conflict +ORDER BY ts; + +SELECT * +FROM json_empty_conflict +ORDER BY ts; + +CREATE TABLE json_empty_type_hints_nullable ( + ts TIMESTAMP TIME INDEX, + j JSON2 ( + user.age BIGINT NULL + ) +) WITH ( + 'append_mode' = 'true' +); + +INSERT INTO json_empty_type_hints_nullable +VALUES (1, '{}'); + +ADMIN FLUSH_TABLE('json_empty_type_hints_nullable'); + +INSERT INTO json_empty_type_hints_nullable +VALUES (2, '{}'); + +SELECT j.user.age +FROM json_empty_type_hints_nullable +ORDER BY ts; + +CREATE TABLE json_empty_type_hints_default ( + ts TIMESTAMP TIME INDEX, + j JSON2 ( + user.age BIGINT NULL DEFAULT 18 + ) +) WITH ( + 'append_mode' = 'true' +); + +INSERT INTO json_empty_type_hints_default +VALUES (1, '{}'); + +ADMIN FLUSH_TABLE('json_empty_type_hints_default'); + +INSERT INTO json_empty_type_hints_default +VALUES (2, '{}'); + +SELECT j.user.age +FROM json_empty_type_hints_default +ORDER BY ts; + +CREATE TABLE json_empty_type_hints_required ( + ts TIMESTAMP TIME INDEX, + j JSON2 ( + user.age BIGINT NOT NULL + ) +) WITH ( + 'append_mode' = 'true' +); + +INSERT INTO json_empty_type_hints_required +VALUES (1, '{}'); + +DROP TABLE json_empty_no_type_hints; + +DROP TABLE json_empty_after_object; + +DROP TABLE json_empty_only_sst; + +DROP TABLE json_empty_conflict; + +DROP TABLE json_empty_type_hints_nullable; + +DROP TABLE json_empty_type_hints_default; + +DROP TABLE json_empty_type_hints_required;