diff --git a/extension/json/CMakeLists.txt b/extension/json/CMakeLists.txt index d032d73c35c8..8c2f634c7aee 100644 --- a/extension/json/CMakeLists.txt +++ b/extension/json/CMakeLists.txt @@ -31,6 +31,7 @@ set(JSON_EXTENSION_FILES json_functions/json_serialize_sql.cpp json_functions/read_json.cpp json_functions/read_json_objects.cpp + json_functions/json_array_tail.cpp json_functions/json_array_append.cpp ${YYJSON_OBJECT_FILES}) diff --git a/extension/json/include/json_functions.hpp b/extension/json/include/json_functions.hpp index 427e0efcab57..5e9d0c904964 100644 --- a/extension/json/include/json_functions.hpp +++ b/extension/json/include/json_functions.hpp @@ -102,7 +102,8 @@ class JSONFunctions { static ScalarFunctionSet GetSerializeSqlFunction(); static ScalarFunctionSet GetDeserializeSqlFunction(); - static ScalarFunctionSet GetArrayAppendFunction(); + static ScalarFunctionSet GetArrayTailFunction(); + static ScalarFunctionSet GetArrayAppendFunction(); static PragmaFunctionSet GetExecuteJsonSerializedSqlPragmaFunction(); template diff --git a/extension/json/json_config.py b/extension/json/json_config.py index b6c655e05b71..d4902dca1bb2 100644 --- a/extension/json/json_config.py +++ b/extension/json/json_config.py @@ -32,6 +32,7 @@ 'extension/json/json_serializer.cpp', 'extension/json/json_deserializer.cpp', 'extension/json/serialize_json.cpp', + 'extension/json/json_array_tail.cpp', 'extension/json/json_array_append.cpp', ] ] diff --git a/extension/json/json_functions/json_array_tail.cpp b/extension/json/json_functions/json_array_tail.cpp new file mode 100644 index 000000000000..984f8f334f9a --- /dev/null +++ b/extension/json/json_functions/json_array_tail.cpp @@ -0,0 +1,33 @@ +#include "json_executors.hpp" + +namespace duckdb { + +static void ArrayTailFunction(DataChunk &args, ExpressionState &state, Vector &result) { + JSONExecutors::UnaryMutExecute(args, state, result, + [](yyjson_mut_val *arr, yyjson_mut_doc *doc, yyjson_alc *alc, Vector &result) { + if (!yyjson_mut_is_arr(arr)) { + throw InvalidInputException("JSON input not an JSON Array"); + } + + if (yyjson_mut_arr_size(arr) == 0) { + return yyjson_mut_arr(doc); + } + + yyjson_mut_arr_remove_first(arr); + return arr; + }); +} + +static void GetArrayTailFunctionInternal(ScalarFunctionSet &set, const LogicalType &input_type) { + set.AddFunction(ScalarFunction("json_array_tail", {input_type}, JSONCommon::JSONType(), ArrayTailFunction, nullptr, + nullptr, nullptr, JSONFunctionLocalState::Init)); +} + +ScalarFunctionSet JSONFunctions::GetArrayTailFunction() { + ScalarFunctionSet set("json_array_tail"); + GetArrayTailFunctionInternal(set, LogicalType::VARCHAR); + GetArrayTailFunctionInternal(set, JSONCommon::JSONType()); + return set; +} + +} // namespace duckdb diff --git a/test/sql/json/scalar/test_json_array_tail.test b/test/sql/json/scalar/test_json_array_tail.test new file mode 100644 index 000000000000..939d527db921 --- /dev/null +++ b/test/sql/json/scalar/test_json_array_tail.test @@ -0,0 +1,47 @@ +# name: test/sql/json/scalar/test_json_array_tail.test +# description: Checks whether a given JSON Array's tail is correct +# group: [scalar] + +require json + +statement ok +PRAGMA enable_verification + +# list with varchar to json +statement ok +create table test (j json) + +statement ok +insert into test values('[]'), ('[42]'), ('["a", 42, true]') + +query I +SELECT json_array_tail(j) FROM test +---- +[] +[] +[42,true] + +statement error +SELECT json_array_tail('') +--- +Error: Invalid Input Error: Malformed JSON at byte 0 of input: input length is 0. Input: + +statement error +SELECT json_array_tail('{}') +--- +Error: Invalid Input Error: JSON input not a JSON Array + +statement error +SELECT json_array_tail('true') +--- +Error: Invalid Input Error: JSON input not a JSON Array + +statement error +SELECT json_array_tail('"Hello"') +--- +Error: Invalid Input Error: JSON input not a JSON Array + +statement error +SELECT json_array_tail('42') +--- +Error: Invalid Input Error: JSON input not a JSON Array