diff --git a/src/common/function/src/scalars/json.rs b/src/common/function/src/scalars/json.rs index 26d63d3b45e..279577b4956 100644 --- a/src/common/function/src/scalars/json.rs +++ b/src/common/function/src/scalars/json.rs @@ -14,12 +14,16 @@ use std::sync::Arc; mod json_get; +mod json_is; mod json_to_string; -mod to_json; +mod parse_json; use json_get::{JsonGetBool, JsonGetFloat, JsonGetInt, JsonGetString}; +use json_is::{ + JsonIsArray, JsonIsBool, JsonIsFloat, JsonIsInt, JsonIsNull, JsonIsObject, JsonIsString, +}; use json_to_string::JsonToStringFunction; -use to_json::ToJsonFunction; +use parse_json::ParseJsonFunction; use crate::function_registry::FunctionRegistry; @@ -28,11 +32,19 @@ pub(crate) struct JsonFunction; impl JsonFunction { pub fn register(registry: &FunctionRegistry) { registry.register(Arc::new(JsonToStringFunction)); - registry.register(Arc::new(ToJsonFunction)); + registry.register(Arc::new(ParseJsonFunction)); registry.register(Arc::new(JsonGetInt)); registry.register(Arc::new(JsonGetFloat)); registry.register(Arc::new(JsonGetString)); registry.register(Arc::new(JsonGetBool)); + + registry.register(Arc::new(JsonIsNull)); + registry.register(Arc::new(JsonIsInt)); + registry.register(Arc::new(JsonIsFloat)); + registry.register(Arc::new(JsonIsString)); + registry.register(Arc::new(JsonIsBool)); + registry.register(Arc::new(JsonIsArray)); + registry.register(Arc::new(JsonIsObject)); } } diff --git a/src/common/function/src/scalars/json/json_get.rs b/src/common/function/src/scalars/json/json_get.rs index 78ddc1d2642..d31f7a0c6e4 100644 --- a/src/common/function/src/scalars/json/json_get.rs +++ b/src/common/function/src/scalars/json/json_get.rs @@ -47,7 +47,7 @@ fn get_json_by_path(json: &[u8], path: &str) -> Option> { /// If the path does not exist or the value is not the type specified, return `NULL`. macro_rules! json_get { // e.g. name = JsonGetInt, type = Int64, rust_type = i64, doc = "Get the value from the JSONB by the given path and return it as an integer." - ($name: ident, $type: ident, $rust_type: ident, $doc:expr) => { + ($name:ident, $type:ident, $rust_type:ident, $doc:expr) => { paste::paste! { #[doc = $doc] #[derive(Clone, Debug, Default)] diff --git a/src/common/function/src/scalars/json/json_is.rs b/src/common/function/src/scalars/json/json_is.rs new file mode 100644 index 00000000000..e0580ad9d49 --- /dev/null +++ b/src/common/function/src/scalars/json/json_is.rs @@ -0,0 +1,215 @@ +// Copyright 2023 Greptime Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::{self, Display}; + +use common_query::error::{InvalidFuncArgsSnafu, Result, UnsupportedInputDataTypeSnafu}; +use common_query::prelude::Signature; +use datafusion::logical_expr::Volatility; +use datatypes::data_type::ConcreteDataType; +use datatypes::prelude::VectorRef; +use datatypes::scalars::ScalarVectorBuilder; +use datatypes::vectors::{BooleanVectorBuilder, MutableVector}; +use snafu::ensure; + +use crate::function::{Function, FunctionContext}; + +/// Checks if the input is a JSON object of the given type. +macro_rules! json_is { + ($name:ident, $json_type:ident, $doc:expr) => { + paste::paste! { + #[derive(Clone, Debug, Default)] + pub struct $name; + + impl Function for $name { + fn name(&self) -> &str { + stringify!([<$name:snake>]) + } + + fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result { + Ok(ConcreteDataType::boolean_datatype()) + } + + fn signature(&self) -> Signature { + Signature::exact(vec![ConcreteDataType::json_datatype()], Volatility::Immutable) + } + + fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result { + ensure!( + columns.len() == 1, + InvalidFuncArgsSnafu { + err_msg: format!( + "The length of the args is not correct, expect exactly one, have: {}", + columns.len() + ), + } + ); + + let jsons = &columns[0]; + let size = jsons.len(); + let datatype = jsons.data_type(); + let mut results = BooleanVectorBuilder::with_capacity(size); + + match datatype { + // JSON data type uses binary vector + ConcreteDataType::Binary(_) => { + for i in 0..size { + let json = jsons.get_ref(i); + let json = json.as_binary(); + let result = match json { + Ok(Some(json)) => { + Some(jsonb::[](json)) + } + _ => None, + }; + results.push(result); + } + } + _ => { + return UnsupportedInputDataTypeSnafu { + function: stringify!([<$name:snake>]), + datatypes: columns.iter().map(|c| c.data_type()).collect::>(), + } + .fail(); + } + } + + Ok(results.to_vector()) + } + } + + impl Display for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", stringify!([<$name:snake>]).to_ascii_uppercase()) + } + } + } + } +} + +json_is!(JsonIsNull, null, "Checks if the input JSONB is null"); +json_is!( + JsonIsBool, + boolean, + "Checks if the input JSONB is a boolean type JSON value" +); +json_is!( + JsonIsInt, + i64, + "Checks if the input JSONB is a integer type JSON value" +); +json_is!( + JsonIsFloat, + number, + "Checks if the input JSONB is a JSON float" +); +json_is!( + JsonIsString, + string, + "Checks if the input JSONB is a JSON string" +); +json_is!( + JsonIsArray, + array, + "Checks if the input JSONB is a JSON array" +); +json_is!( + JsonIsObject, + object, + "Checks if the input JSONB is a JSON object" +); + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use datatypes::scalars::ScalarVector; + use datatypes::vectors::BinaryVector; + + use super::*; + + #[test] + fn test_json_is_functions() { + let json_is_functions: [&dyn Function; 6] = [ + &JsonIsBool, + &JsonIsInt, + &JsonIsFloat, + &JsonIsString, + &JsonIsArray, + &JsonIsObject, + ]; + let expected_names = [ + "json_is_bool", + "json_is_int", + "json_is_float", + "json_is_string", + "json_is_array", + "json_is_object", + ]; + for (func, expected_name) in json_is_functions.iter().zip(expected_names.iter()) { + assert_eq!(func.name(), *expected_name); + assert_eq!( + func.return_type(&[ConcreteDataType::json_datatype()]) + .unwrap(), + ConcreteDataType::boolean_datatype() + ); + assert_eq!( + func.signature(), + Signature::exact( + vec![ConcreteDataType::json_datatype()], + Volatility::Immutable + ) + ); + } + + let json_strings = [ + r#"true"#, + r#"1"#, + r#"1.0"#, + r#""The pig fly through a castle, and has been attracted by the princess.""#, + r#"[1, 2]"#, + r#"{"a": 1}"#, + ]; + let expected_results = [ + [true, false, false, false, false, false], + [false, true, false, false, false, false], + // Integers are also floats + [false, true, true, false, false, false], + [false, false, false, true, false, false], + [false, false, false, false, true, false], + [false, false, false, false, false, true], + ]; + + let jsonbs = json_strings + .iter() + .map(|s| { + let value = jsonb::parse_value(s.as_bytes()).unwrap(); + value.to_vec() + }) + .collect::>(); + let json_vector = BinaryVector::from_vec(jsonbs); + let args: Vec = vec![Arc::new(json_vector)]; + + for (func, expected_result) in json_is_functions.iter().zip(expected_results.iter()) { + let vector = func.eval(FunctionContext::default(), &args).unwrap(); + assert_eq!(vector.len(), json_strings.len()); + + for (i, expected) in expected_result.iter().enumerate() { + let result = vector.get_ref(i); + let result = result.as_boolean().unwrap().unwrap(); + assert_eq!(result, *expected); + } + } + } +} diff --git a/src/common/function/src/scalars/json/json_to_string.rs b/src/common/function/src/scalars/json/json_to_string.rs index 8a5e569a149..9873000d6e2 100644 --- a/src/common/function/src/scalars/json/json_to_string.rs +++ b/src/common/function/src/scalars/json/json_to_string.rs @@ -119,7 +119,7 @@ mod tests { use super::*; #[test] - fn test_get_by_path_function() { + fn test_json_to_string_function() { let json_to_string = JsonToStringFunction; assert_eq!("json_to_string", json_to_string.name()); diff --git a/src/common/function/src/scalars/json/to_json.rs b/src/common/function/src/scalars/json/parse_json.rs similarity index 92% rename from src/common/function/src/scalars/json/to_json.rs rename to src/common/function/src/scalars/json/parse_json.rs index 9c3cc90b66c..64300838d8f 100644 --- a/src/common/function/src/scalars/json/to_json.rs +++ b/src/common/function/src/scalars/json/parse_json.rs @@ -27,11 +27,11 @@ use crate::function::{Function, FunctionContext}; /// Parses the `String` into `JSONB`. #[derive(Clone, Debug, Default)] -pub struct ToJsonFunction; +pub struct ParseJsonFunction; -const NAME: &str = "to_json"; +const NAME: &str = "parse_json"; -impl Function for ToJsonFunction { +impl Function for ParseJsonFunction { fn name(&self) -> &str { NAME } @@ -101,9 +101,9 @@ impl Function for ToJsonFunction { } } -impl Display for ToJsonFunction { +impl Display for ParseJsonFunction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "TO_JSON") + write!(f, "PARSE_JSON") } } @@ -119,17 +119,17 @@ mod tests { #[test] fn test_get_by_path_function() { - let to_json = ToJsonFunction; + let parse_json = ParseJsonFunction; - assert_eq!("to_json", to_json.name()); + assert_eq!("parse_json", parse_json.name()); assert_eq!( ConcreteDataType::json_datatype(), - to_json + parse_json .return_type(&[ConcreteDataType::json_datatype()]) .unwrap() ); - assert!(matches!(to_json.signature(), + assert!(matches!(parse_json.signature(), Signature { type_signature: TypeSignature::Exact(valid_types), volatility: Volatility::Immutable @@ -152,13 +152,12 @@ mod tests { let json_string_vector = StringVector::from_vec(json_strings.to_vec()); let args: Vec = vec![Arc::new(json_string_vector)]; - let vector = to_json.eval(FunctionContext::default(), &args).unwrap(); + let vector = parse_json.eval(FunctionContext::default(), &args).unwrap(); assert_eq!(3, vector.len()); for (i, gt) in jsonbs.iter().enumerate() { let result = vector.get_ref(i); let result = result.as_binary().unwrap().unwrap(); - // remove whitespaces assert_eq!(gt, result); } } diff --git a/tests/cases/standalone/common/function/json.sql b/tests/cases/standalone/common/function/json.sql deleted file mode 100644 index c6214ae0f8b..00000000000 --- a/tests/cases/standalone/common/function/json.sql +++ /dev/null @@ -1,56 +0,0 @@ --- json_get functions -- -SELECT json_get_int(to_json('{"a": {"b": {"c": 1}}}'), 'a.b.c'); - -SELECT json_get_float(to_json('{"a": {"b": {"c": 1.234}}}'), 'a:b.c'); - -SELECT json_get_string(to_json('{"a": {"b": {"c": "foo"}}}'), 'a.b:c'); - -SELECT json_get_bool(to_json('{"a": {"b": {"c": true}}}'), 'a.b["c"]'); - -SELECT json_get_int(to_json('{"a": {"b": {"c": {"d": 1}}}}'), 'a.b'); - -SELECT json_get_string(to_json('{"a": {"b": {"c": {"d": 1}}}}'), 'a.b'); - --- test functions with table rows -- -CREATE TABLE jsons(j JSON, ts timestamp time index); - -INSERT INTO jsons VALUES(to_json('{"a": {"b": {"c": 1}}}'), 1); - -INSERT INTO jsons VALUES(to_json('{"a": {"b": {"c": 1.234}}}'), 2); - -INSERT INTO jsons VALUES(to_json('{"a": {"b": {"c": "foo"}}}'), 3); - -INSERT INTO jsons VALUES(to_json('{"a": {"b": {"c": true}}}'), 4); - -SELECT json_get_int(j, 'a.b.c') FROM jsons; - -SELECT json_get_float(j, 'a["b"].c') FROM jsons; - -SELECT json_get_string(j, 'a.b.c?(@ == 1)') FROM jsons; - -SELECT json_get_bool(j, 'a.b.c') FROM jsons; - -SELECT json_get_int(j, 'a.b["c"]') FROM jsons; - -DROP TABLE jsons; - --- test functions with arrays -- -CREATE TABLE jsons(j JSON, ts timestamp time index); - -INSERT INTO jsons VALUES(to_json('["a", "bcde", "", "Long time ago, there is a little pig flying in the sky"]'), 1); - -INSERT INTO jsons VALUES(to_json('[true, false, false, false]'), 2); - -INSERT INTO jsons VALUES(to_json('[1, 0, -2147483649, 2147483648]'), 3); - -INSERT INTO jsons VALUES(to_json('[1.2, 3.1415926535897932384626, -3e123, 1e100]'), 4); - -SELECT json_get_int(j, '[0]') FROM jsons; - -SELECT json_get_float(j, '[1]') FROM jsons; - -SELECT json_get_bool(j, '[2]') FROM jsons; - -SELECT json_get_string(j, '[3]') FROM jsons; - -DROP TABLE jsons; diff --git a/tests/cases/standalone/common/function/json.result b/tests/cases/standalone/common/function/json/json_get.result similarity index 61% rename from tests/cases/standalone/common/function/json.result rename to tests/cases/standalone/common/function/json/json_get.result index f2a59b9d706..01767387a9f 100644 --- a/tests/cases/standalone/common/function/json.result +++ b/tests/cases/standalone/common/function/json/json_get.result @@ -1,70 +1,70 @@ -- json_get functions -- -SELECT json_get_int(to_json('{"a": {"b": {"c": 1}}}'), 'a.b.c'); +SELECT json_get_int(parse_json('{"a": {"b": {"c": 1}}}'), 'a.b.c'); -+---------------------------------------------------------------------+ -| json_get_int(to_json(Utf8("{"a": {"b": {"c": 1}}}")),Utf8("a.b.c")) | -+---------------------------------------------------------------------+ -| 1 | -+---------------------------------------------------------------------+ ++------------------------------------------------------------------------+ +| json_get_int(parse_json(Utf8("{"a": {"b": {"c": 1}}}")),Utf8("a.b.c")) | ++------------------------------------------------------------------------+ +| 1 | ++------------------------------------------------------------------------+ -SELECT json_get_float(to_json('{"a": {"b": {"c": 1.234}}}'), 'a:b.c'); +SELECT json_get_float(parse_json('{"a": {"b": {"c": 1.234}}}'), 'a:b.c'); -+---------------------------------------------------------------------------+ -| json_get_float(to_json(Utf8("{"a": {"b": {"c": 1.234}}}")),Utf8("a:b.c")) | -+---------------------------------------------------------------------------+ -| 1.234 | -+---------------------------------------------------------------------------+ ++------------------------------------------------------------------------------+ +| json_get_float(parse_json(Utf8("{"a": {"b": {"c": 1.234}}}")),Utf8("a:b.c")) | ++------------------------------------------------------------------------------+ +| 1.234 | ++------------------------------------------------------------------------------+ -SELECT json_get_string(to_json('{"a": {"b": {"c": "foo"}}}'), 'a.b:c'); +SELECT json_get_string(parse_json('{"a": {"b": {"c": "foo"}}}'), 'a.b:c'); -+----------------------------------------------------------------------------+ -| json_get_string(to_json(Utf8("{"a": {"b": {"c": "foo"}}}")),Utf8("a.b:c")) | -+----------------------------------------------------------------------------+ -| foo | -+----------------------------------------------------------------------------+ ++-------------------------------------------------------------------------------+ +| json_get_string(parse_json(Utf8("{"a": {"b": {"c": "foo"}}}")),Utf8("a.b:c")) | ++-------------------------------------------------------------------------------+ +| foo | ++-------------------------------------------------------------------------------+ -SELECT json_get_bool(to_json('{"a": {"b": {"c": true}}}'), 'a.b["c"]'); +SELECT json_get_bool(parse_json('{"a": {"b": {"c": true}}}'), 'a.b["c"]'); -+----------------------------------------------------------------------------+ -| json_get_bool(to_json(Utf8("{"a": {"b": {"c": true}}}")),Utf8("a.b["c"]")) | -+----------------------------------------------------------------------------+ -| true | -+----------------------------------------------------------------------------+ ++-------------------------------------------------------------------------------+ +| json_get_bool(parse_json(Utf8("{"a": {"b": {"c": true}}}")),Utf8("a.b["c"]")) | ++-------------------------------------------------------------------------------+ +| true | ++-------------------------------------------------------------------------------+ -SELECT json_get_int(to_json('{"a": {"b": {"c": {"d": 1}}}}'), 'a.b'); - -+--------------------------------------------------------------------------+ -| json_get_int(to_json(Utf8("{"a": {"b": {"c": {"d": 1}}}}")),Utf8("a.b")) | -+--------------------------------------------------------------------------+ -| | -+--------------------------------------------------------------------------+ - -SELECT json_get_string(to_json('{"a": {"b": {"c": {"d": 1}}}}'), 'a.b'); +SELECT json_get_int(parse_json('{"a": {"b": {"c": {"d": 1}}}}'), 'a.b'); +-----------------------------------------------------------------------------+ -| json_get_string(to_json(Utf8("{"a": {"b": {"c": {"d": 1}}}}")),Utf8("a.b")) | +| json_get_int(parse_json(Utf8("{"a": {"b": {"c": {"d": 1}}}}")),Utf8("a.b")) | +-----------------------------------------------------------------------------+ | | +-----------------------------------------------------------------------------+ +SELECT json_get_string(parse_json('{"a": {"b": {"c": {"d": 1}}}}'), 'a.b'); + ++--------------------------------------------------------------------------------+ +| json_get_string(parse_json(Utf8("{"a": {"b": {"c": {"d": 1}}}}")),Utf8("a.b")) | ++--------------------------------------------------------------------------------+ +| | ++--------------------------------------------------------------------------------+ + -- test functions with table rows -- CREATE TABLE jsons(j JSON, ts timestamp time index); Affected Rows: 0 -INSERT INTO jsons VALUES(to_json('{"a": {"b": {"c": 1}}}'), 1); +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": 1}}}'), 1); Affected Rows: 1 -INSERT INTO jsons VALUES(to_json('{"a": {"b": {"c": 1.234}}}'), 2); +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": 1.234}}}'), 2); Affected Rows: 1 -INSERT INTO jsons VALUES(to_json('{"a": {"b": {"c": "foo"}}}'), 3); +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": "foo"}}}'), 3); Affected Rows: 1 -INSERT INTO jsons VALUES(to_json('{"a": {"b": {"c": true}}}'), 4); +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": true}}}'), 4); Affected Rows: 1 @@ -132,19 +132,19 @@ CREATE TABLE jsons(j JSON, ts timestamp time index); Affected Rows: 0 -INSERT INTO jsons VALUES(to_json('["a", "bcde", "", "Long time ago, there is a little pig flying in the sky"]'), 1); +INSERT INTO jsons VALUES(parse_json('["a", "bcde", "", "Long time ago, there is a little pig flying in the sky"]'), 1); Affected Rows: 1 -INSERT INTO jsons VALUES(to_json('[true, false, false, false]'), 2); +INSERT INTO jsons VALUES(parse_json('[true, false, false, false]'), 2); Affected Rows: 1 -INSERT INTO jsons VALUES(to_json('[1, 0, -2147483649, 2147483648]'), 3); +INSERT INTO jsons VALUES(parse_json('[1, 0, -2147483649, 2147483648]'), 3); Affected Rows: 1 -INSERT INTO jsons VALUES(to_json('[1.2, 3.1415926535897932384626, -3e123, 1e100]'), 4); +INSERT INTO jsons VALUES(parse_json('[1.2, 3.1415926535897932384626, -3e123, 1e100]'), 4); Affected Rows: 1 @@ -196,3 +196,70 @@ DROP TABLE jsons; Affected Rows: 0 +-- test functions in WHERE clause -- +CREATE TABLE jsons(j JSON, ts timestamp time index); + +Affected Rows: 0 + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": 1}}}'), 1); + +Affected Rows: 1 + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": 1.234}}}'), 2); + +Affected Rows: 1 + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": "foo"}}}'), 3); + +Affected Rows: 1 + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": true}}}'), 4); + +Affected Rows: 1 + +SELECT json_to_string(j) FROM jsons WHERE json_get_int(j, 'a.b.c') = 1; + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| {"a":{"b":{"c":1}}} | +| {"a":{"b":{"c":true}}} | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE json_get_float(j, 'a.b.c') = 1.234; + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| {"a":{"b":{"c":1.234}}} | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE json_get_string(j, 'a.b.c') = 'foo'; + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| {"a":{"b":{"c":"foo"}}} | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE json_get_bool(j, 'a.b.c') = true; + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| {"a":{"b":{"c":true}}} | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE CAST(json_get_int(j, 'a.b.c') AS BOOLEAN); + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| {"a":{"b":{"c":1}}} | +| {"a":{"b":{"c":true}}} | ++-------------------------+ + +DROP TABLE jsons; + +Affected Rows: 0 + diff --git a/tests/cases/standalone/common/function/json/json_get.sql b/tests/cases/standalone/common/function/json/json_get.sql new file mode 100644 index 00000000000..3247536b077 --- /dev/null +++ b/tests/cases/standalone/common/function/json/json_get.sql @@ -0,0 +1,79 @@ +-- json_get functions -- +SELECT json_get_int(parse_json('{"a": {"b": {"c": 1}}}'), 'a.b.c'); + +SELECT json_get_float(parse_json('{"a": {"b": {"c": 1.234}}}'), 'a:b.c'); + +SELECT json_get_string(parse_json('{"a": {"b": {"c": "foo"}}}'), 'a.b:c'); + +SELECT json_get_bool(parse_json('{"a": {"b": {"c": true}}}'), 'a.b["c"]'); + +SELECT json_get_int(parse_json('{"a": {"b": {"c": {"d": 1}}}}'), 'a.b'); + +SELECT json_get_string(parse_json('{"a": {"b": {"c": {"d": 1}}}}'), 'a.b'); + +-- test functions with table rows -- +CREATE TABLE jsons(j JSON, ts timestamp time index); + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": 1}}}'), 1); + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": 1.234}}}'), 2); + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": "foo"}}}'), 3); + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": true}}}'), 4); + +SELECT json_get_int(j, 'a.b.c') FROM jsons; + +SELECT json_get_float(j, 'a["b"].c') FROM jsons; + +SELECT json_get_string(j, 'a.b.c?(@ == 1)') FROM jsons; + +SELECT json_get_bool(j, 'a.b.c') FROM jsons; + +SELECT json_get_int(j, 'a.b["c"]') FROM jsons; + +DROP TABLE jsons; + +-- test functions with arrays -- +CREATE TABLE jsons(j JSON, ts timestamp time index); + +INSERT INTO jsons VALUES(parse_json('["a", "bcde", "", "Long time ago, there is a little pig flying in the sky"]'), 1); + +INSERT INTO jsons VALUES(parse_json('[true, false, false, false]'), 2); + +INSERT INTO jsons VALUES(parse_json('[1, 0, -2147483649, 2147483648]'), 3); + +INSERT INTO jsons VALUES(parse_json('[1.2, 3.1415926535897932384626, -3e123, 1e100]'), 4); + +SELECT json_get_int(j, '[0]') FROM jsons; + +SELECT json_get_float(j, '[1]') FROM jsons; + +SELECT json_get_bool(j, '[2]') FROM jsons; + +SELECT json_get_string(j, '[3]') FROM jsons; + +DROP TABLE jsons; + +-- test functions in WHERE clause -- +CREATE TABLE jsons(j JSON, ts timestamp time index); + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": 1}}}'), 1); + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": 1.234}}}'), 2); + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": "foo"}}}'), 3); + +INSERT INTO jsons VALUES(parse_json('{"a": {"b": {"c": true}}}'), 4); + +SELECT json_to_string(j) FROM jsons WHERE json_get_int(j, 'a.b.c') = 1; + +SELECT json_to_string(j) FROM jsons WHERE json_get_float(j, 'a.b.c') = 1.234; + +SELECT json_to_string(j) FROM jsons WHERE json_get_string(j, 'a.b.c') = 'foo'; + +SELECT json_to_string(j) FROM jsons WHERE json_get_bool(j, 'a.b.c') = true; + +SELECT json_to_string(j) FROM jsons WHERE CAST(json_get_int(j, 'a.b.c') AS BOOLEAN); + +DROP TABLE jsons; diff --git a/tests/cases/standalone/common/function/json/json_is.result b/tests/cases/standalone/common/function/json/json_is.result new file mode 100644 index 00000000000..d162fa16116 --- /dev/null +++ b/tests/cases/standalone/common/function/json/json_is.result @@ -0,0 +1,175 @@ +-- json_is functions -- +SELECT json_is_object(parse_json('{"a": 1}')); + ++----------------------------------------------+ +| json_is_object(parse_json(Utf8("{"a": 1}"))) | ++----------------------------------------------+ +| true | ++----------------------------------------------+ + +SELECT json_is_array(parse_json('[1, 2, 3]')); + ++----------------------------------------------+ +| json_is_array(parse_json(Utf8("[1, 2, 3]"))) | ++----------------------------------------------+ +| true | ++----------------------------------------------+ + +SELECT json_is_int(parse_json('1')); + ++------------------------------------+ +| json_is_int(parse_json(Utf8("1"))) | ++------------------------------------+ +| true | ++------------------------------------+ + +SELECT json_is_bool(parse_json('true')); + ++----------------------------------------+ +| json_is_bool(parse_json(Utf8("true"))) | ++----------------------------------------+ +| true | ++----------------------------------------+ + +SELECT json_is_null(parse_json('null')); + ++----------------------------------------+ +| json_is_null(parse_json(Utf8("null"))) | ++----------------------------------------+ +| true | ++----------------------------------------+ + +SELECT json_is_float(parse_json('1.2')); + ++----------------------------------------+ +| json_is_float(parse_json(Utf8("1.2"))) | ++----------------------------------------+ +| true | ++----------------------------------------+ + +SELECT json_is_string(parse_json('"foo"')); + ++-------------------------------------------+ +| json_is_string(parse_json(Utf8(""foo""))) | ++-------------------------------------------+ +| true | ++-------------------------------------------+ + +SELECT json_is_null(parse_json('{"a": 1}')); + ++--------------------------------------------+ +| json_is_null(parse_json(Utf8("{"a": 1}"))) | ++--------------------------------------------+ +| false | ++--------------------------------------------+ + +SELECT json_is_string(parse_json('[1, 2, 3]')); + ++-----------------------------------------------+ +| json_is_string(parse_json(Utf8("[1, 2, 3]"))) | ++-----------------------------------------------+ +| false | ++-----------------------------------------------+ + +SELECT json_is_float(parse_json('1')); + ++--------------------------------------+ +| json_is_float(parse_json(Utf8("1"))) | ++--------------------------------------+ +| true | ++--------------------------------------+ + +-- test json_is functions in table rows and WHERE clause -- +CREATE TABLE jsons(j JSON, ts timestamp time index); + +Affected Rows: 0 + +INSERT INTO jsons VALUES(parse_json('{"a": 1}'), 1); + +Affected Rows: 1 + +INSERT INTO jsons VALUES(parse_json('[1, 2, 3]'), 2); + +Affected Rows: 1 + +INSERT INTO jsons VALUES(parse_json('1'), 3); + +Affected Rows: 1 + +INSERT INTO jsons VALUES(parse_json('true'), 4); + +Affected Rows: 1 + +INSERT INTO jsons VALUES(parse_json('null'), 5); + +Affected Rows: 1 + +INSERT INTO jsons VALUES(parse_json('1.2'), 6); + +Affected Rows: 1 + +INSERT INTO jsons VALUES(parse_json('"foo"'), 7); + +Affected Rows: 1 + +SELECT json_to_string(j) FROM jsons WHERE json_is_object(j); + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| {"a":1} | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE json_is_array(j); + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| [1,2,3] | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE json_is_int(j); + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| 1 | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE json_is_bool(j); + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| true | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE json_is_null(j); + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| null | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE json_is_float(j); + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| 1 | +| 1.2 | ++-------------------------+ + +SELECT json_to_string(j) FROM jsons WHERE json_is_string(j); + ++-------------------------+ +| json_to_string(jsons.j) | ++-------------------------+ +| "foo" | ++-------------------------+ + +DROP TABLE jsons; + +Affected Rows: 0 + diff --git a/tests/cases/standalone/common/function/json/json_is.sql b/tests/cases/standalone/common/function/json/json_is.sql new file mode 100644 index 00000000000..72e88d1da26 --- /dev/null +++ b/tests/cases/standalone/common/function/json/json_is.sql @@ -0,0 +1,53 @@ +-- json_is functions -- +SELECT json_is_object(parse_json('{"a": 1}')); + +SELECT json_is_array(parse_json('[1, 2, 3]')); + +SELECT json_is_int(parse_json('1')); + +SELECT json_is_bool(parse_json('true')); + +SELECT json_is_null(parse_json('null')); + +SELECT json_is_float(parse_json('1.2')); + +SELECT json_is_string(parse_json('"foo"')); + +SELECT json_is_null(parse_json('{"a": 1}')); + +SELECT json_is_string(parse_json('[1, 2, 3]')); + +SELECT json_is_float(parse_json('1')); + +-- test json_is functions in table rows and WHERE clause -- +CREATE TABLE jsons(j JSON, ts timestamp time index); + +INSERT INTO jsons VALUES(parse_json('{"a": 1}'), 1); + +INSERT INTO jsons VALUES(parse_json('[1, 2, 3]'), 2); + +INSERT INTO jsons VALUES(parse_json('1'), 3); + +INSERT INTO jsons VALUES(parse_json('true'), 4); + +INSERT INTO jsons VALUES(parse_json('null'), 5); + +INSERT INTO jsons VALUES(parse_json('1.2'), 6); + +INSERT INTO jsons VALUES(parse_json('"foo"'), 7); + +SELECT json_to_string(j) FROM jsons WHERE json_is_object(j); + +SELECT json_to_string(j) FROM jsons WHERE json_is_array(j); + +SELECT json_to_string(j) FROM jsons WHERE json_is_int(j); + +SELECT json_to_string(j) FROM jsons WHERE json_is_bool(j); + +SELECT json_to_string(j) FROM jsons WHERE json_is_null(j); + +SELECT json_to_string(j) FROM jsons WHERE json_is_float(j); + +SELECT json_to_string(j) FROM jsons WHERE json_is_string(j); + +DROP TABLE jsons; diff --git a/tests/cases/standalone/common/types/json/json.result b/tests/cases/standalone/common/types/json/json.result index 710d0b230ac..8392b4cb4a9 100644 --- a/tests/cases/standalone/common/types/json/json.result +++ b/tests/cases/standalone/common/types/json/json.result @@ -41,19 +41,19 @@ INSERT INTO jsons VALUES('[null]', 0), Affected Rows: 12 -INSERT INTO jsons VALUES(to_json('[null]'), 12), -(to_json('[true]'), 13), -(to_json('[false]'), 14), -(to_json('[0]'), 15), -(to_json('["foo"]'), 16), -(to_json('[]'), 17), -(to_json('{}'), 18), -(to_json('[0,1]'), 19), -(to_json('{"foo":"bar"}'), 20), -(to_json('{"a":null,"foo":"bar"}'), 21), -(to_json('[-1]'), 22), -(to_json('[-2147483648]'), 23), -(to_json('{"entities": { +INSERT INTO jsons VALUES(parse_json('[null]'), 12), +(parse_json('[true]'), 13), +(parse_json('[false]'), 14), +(parse_json('[0]'), 15), +(parse_json('["foo"]'), 16), +(parse_json('[]'), 17), +(parse_json('{}'), 18), +(parse_json('[0,1]'), 19), +(parse_json('{"foo":"bar"}'), 20), +(parse_json('{"a":null,"foo":"bar"}'), 21), +(parse_json('[-1]'), 22), +(parse_json('[-2147483648]'), 23), +(parse_json('{"entities": { "description": { "urls": [ { @@ -117,11 +117,11 @@ DELETE FROM jsons; Affected Rows: 25 -INSERT INTO jsons VALUES(to_json('{"a":1, "b":2, "c":3'), 4); +INSERT INTO jsons VALUES(parse_json('{"a":1, "b":2, "c":3'), 4); Error: 3001(EngineExecuteQuery), DataFusion error: Invalid function args: Cannot convert the string to json, have: {"a":1, "b":2, "c":3 -INSERT INTO jsons VALUES(to_json('Morning my friends, have a nice day :)'), 5); +INSERT INTO jsons VALUES(parse_json('Morning my friends, have a nice day :)'), 5); Error: 3001(EngineExecuteQuery), DataFusion error: Invalid function args: Cannot convert the string to json, have: Morning my friends, have a nice day :) diff --git a/tests/cases/standalone/common/types/json/json.sql b/tests/cases/standalone/common/types/json/json.sql index 57fce9a8eab..868edc59e85 100644 --- a/tests/cases/standalone/common/types/json/json.sql +++ b/tests/cases/standalone/common/types/json/json.sql @@ -37,19 +37,19 @@ INSERT INTO jsons VALUES('[null]', 0), } }}', 11); -INSERT INTO jsons VALUES(to_json('[null]'), 12), -(to_json('[true]'), 13), -(to_json('[false]'), 14), -(to_json('[0]'), 15), -(to_json('["foo"]'), 16), -(to_json('[]'), 17), -(to_json('{}'), 18), -(to_json('[0,1]'), 19), -(to_json('{"foo":"bar"}'), 20), -(to_json('{"a":null,"foo":"bar"}'), 21), -(to_json('[-1]'), 22), -(to_json('[-2147483648]'), 23), -(to_json('{"entities": { +INSERT INTO jsons VALUES(parse_json('[null]'), 12), +(parse_json('[true]'), 13), +(parse_json('[false]'), 14), +(parse_json('[0]'), 15), +(parse_json('["foo"]'), 16), +(parse_json('[]'), 17), +(parse_json('{}'), 18), +(parse_json('[0,1]'), 19), +(parse_json('{"foo":"bar"}'), 20), +(parse_json('{"a":null,"foo":"bar"}'), 21), +(parse_json('[-1]'), 22), +(parse_json('[-2147483648]'), 23), +(parse_json('{"entities": { "description": { "urls": [ { @@ -79,9 +79,9 @@ SELECT json_to_string(j), t FROM jsons; --Insert invalid json strings-- DELETE FROM jsons; -INSERT INTO jsons VALUES(to_json('{"a":1, "b":2, "c":3'), 4); +INSERT INTO jsons VALUES(parse_json('{"a":1, "b":2, "c":3'), 4); -INSERT INTO jsons VALUES(to_json('Morning my friends, have a nice day :)'), 5); +INSERT INTO jsons VALUES(parse_json('Morning my friends, have a nice day :)'), 5); SELECT json_to_string(j), t FROM jsons;