Simplify template implementation.

This commit is contained in:
Vitaly Baranov 2019-05-14 02:44:55 +03:00
parent 76bda0342b
commit c93bd31695
8 changed files with 1155 additions and 688 deletions

View File

@ -430,6 +430,13 @@ inline bool_if_safe_conversion<A, B> greaterOrEqualsOp(A a, B b)
template <typename From, typename To>
inline bool NO_SANITIZE_UNDEFINED convertNumeric(From value, To & result)
{
/// If the type is actually the same it's not necessary to do any checks.
if constexpr (std::is_same_v<From, To>)
{
result = value;
return true;
}
/// Note that NaNs doesn't compare equal to anything, but they are still in range of any Float type.
if (isNaN(value) && std::is_floating_point_v<To>)
{

View File

@ -0,0 +1,46 @@
#pragma once
#include <common/StringRef.h>
#include <Common/Exception.h>
#include <Core/Types.h>
namespace DB
{
namespace ErrorCodes
{
extern const int NOT_IMPLEMENTED;
}
/// This class can be used as an argument for the template class FunctionJSON when we unable to parse JSONs.
/// It can't do anything useful and just throws an exception.
struct DummyJSONParser
{
static constexpr bool need_preallocate = false;
void preallocate(size_t) {}
bool parse(const char *, size_t) { throw Exception{"Functions JSON* are not supported without AVX2", ErrorCodes::NOT_IMPLEMENTED}; }
using Iterator = std::nullptr_t;
Iterator getRoot() const { return nullptr; }
static bool downToArray(Iterator &) { return false; }
static bool downToObject(Iterator &) { return false; }
static bool downToObject(Iterator &, StringRef &) { return false; }
static bool parentScopeIsObject(const Iterator &) { return false; }
static bool next(Iterator &) { return false; }
static bool nextKeyValue(Iterator &) { return false; }
static bool nextKeyValue(Iterator &, StringRef &) { return false; }
static bool isInteger(const Iterator &) { return false; }
static bool isFloat(const Iterator &) { return false; }
static bool isString(const Iterator &) { return false; }
static bool isArray(const Iterator &) { return false; }
static bool isObject(const Iterator &) { return false; }
static bool isBool(const Iterator &) { return false; }
static bool isNull(const Iterator &) { return false; }
static StringRef getKey(const Iterator &) { return {}; }
static StringRef getString(const Iterator &) { return {}; }
static Int64 getInteger(const Iterator &) { return 0; }
static double getFloat(const Iterator &) { return 0; }
static bool getBool(const Iterator &) { return false; }
};
}

View File

@ -1,459 +1,7 @@
#include <Functions/FunctionsJSON.h>
#include <Functions/FunctionFactory.h>
#include <Common/config.h>
#include <Functions/DummyJSONParser.h>
#include <Functions/SimdJSONParser.h>
#if USE_SIMDJSON
#include <DataTypes/DataTypeArray.h>
#include <DataTypes/DataTypeNullable.h>
#include <DataTypes/DataTypeString.h>
#include <DataTypes/DataTypeTuple.h>
#include <DataTypes/DataTypesNumber.h>
#include <DataTypes/DataTypeEnum.h>
namespace DB
{
template <typename T>
class JSONNullableImplBase
{
public:
static DataTypePtr getType() { return std::make_shared<DataTypeNullable>(std::make_shared<T>()); }
static Field getDefault() { return {}; }
};
class JSONHasImpl : public JSONNullableImplBase<DataTypeUInt8>
{
public:
static constexpr auto name{"JSONHas"};
static Field getValue(ParsedJson::iterator &) { return {1}; }
};
class JSONLengthImpl : public JSONNullableImplBase<DataTypeUInt64>
{
public:
static constexpr auto name{"JSONLength"};
static Field getValue(ParsedJson::iterator & pjh)
{
if (!pjh.is_object_or_array())
return getDefault();
size_t size = 0;
if (pjh.down())
{
++size;
while (pjh.next())
++size;
if (pjh.get_scope_type() == '{')
size /= 2;
}
return {size};
}
};
class JSONTypeImpl
{
public:
static constexpr auto name{"JSONType"};
static DataTypePtr getType()
{
static const std::vector<std::pair<String, Int8>> values = {
{"Array", '['},
{"Object", '{'},
{"String", '"'},
{"Int", 'l'},
{"Float",'d'},
{"Bool", 'b'},
{"Null",'n'},
};
return std::make_shared<DataTypeNullable>(std::make_shared<DataTypeEnum<Int8>>(values));
}
static Field getDefault() { return {}; }
static Field getValue(ParsedJson::iterator & pjh)
{
switch (pjh.get_type())
{
case '[':
case '{':
case '"':
case 'l':
case 'd':
case 'n':
return {pjh.get_type()};
case 't':
case 'f':
return {'b'};
default:
return {};
}
}
};
class JSONExtractImpl
{
public:
static constexpr auto name{"JSONExtract"};
static DataTypePtr getType(const DataTypePtr & type)
{
WhichDataType which{type};
if (which.isNativeUInt() || which.isNativeInt() || which.isFloat() || which.isEnum() || which.isDateOrDateTime()
|| which.isStringOrFixedString() || which.isInterval())
return std::make_shared<DataTypeNullable>(type);
if (which.isArray())
{
auto array_type = static_cast<const DataTypeArray *>(type.get());
return std::make_shared<DataTypeArray>(getType(array_type->getNestedType()));
}
if (which.isTuple())
{
auto tuple_type = static_cast<const DataTypeTuple *>(type.get());
DataTypes types;
types.reserve(tuple_type->getElements().size());
for (const DataTypePtr & element : tuple_type->getElements())
{
types.push_back(getType(element));
}
return std::make_shared<DataTypeTuple>(std::move(types));
}
throw Exception{"Unsupported return type schema: " + type->getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
}
static Field getDefault(const DataTypePtr & type)
{
WhichDataType which{type};
if (which.isNativeUInt() || which.isNativeInt() || which.isFloat() || which.isEnum() || which.isDateOrDateTime()
|| which.isStringOrFixedString() || which.isInterval())
return {};
if (which.isArray())
return {Array{}};
if (which.isTuple())
{
auto tuple_type = static_cast<const DataTypeTuple *>(type.get());
Tuple tuple;
tuple.toUnderType().reserve(tuple_type->getElements().size());
for (const DataTypePtr & element : tuple_type->getElements())
tuple.toUnderType().push_back(getDefault(element));
return {tuple};
}
// should not reach
throw Exception{"Unsupported return type schema: " + type->getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
}
static Field getValue(ParsedJson::iterator & pjh, const DataTypePtr & type)
{
WhichDataType which{type};
if (which.isNativeUInt() || which.isNativeInt() || which.isEnum() || which.isDateOrDateTime() || which.isInterval())
{
if (pjh.is_integer())
return {pjh.get_integer()};
else
return getDefault(type);
}
if (which.isFloat())
{
if (pjh.is_integer())
return {static_cast<double>(pjh.get_integer())};
else if (pjh.is_double())
return {pjh.get_double()};
else
return getDefault(type);
}
if (which.isStringOrFixedString())
{
if (pjh.is_string())
return {String{pjh.get_string()}};
else
return getDefault(type);
}
if (which.isArray())
{
if (!pjh.is_object_or_array())
return getDefault(type);
auto array_type = static_cast<const DataTypeArray *>(type.get());
Array array;
bool first = true;
while (first ? pjh.down() : pjh.next())
{
first = false;
ParsedJson::iterator pjh1{pjh};
array.push_back(getValue(pjh1, array_type->getNestedType()));
}
return {array};
}
if (which.isTuple())
{
if (!pjh.is_object_or_array())
return getDefault(type);
auto tuple_type = static_cast<const DataTypeTuple *>(type.get());
Tuple tuple;
tuple.toUnderType().reserve(tuple_type->getElements().size());
bool valid = true;
bool first = true;
for (const DataTypePtr & element : tuple_type->getElements())
{
if (valid)
{
valid &= first ? pjh.down() : pjh.next();
first = false;
ParsedJson::iterator pjh1{pjh};
tuple.toUnderType().push_back(getValue(pjh1, element));
}
else
tuple.toUnderType().push_back(getDefault(element));
}
return {tuple};
}
// should not reach
throw Exception{"Unsupported return type schema: " + type->getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
}
};
class JSONExtractUIntImpl : public JSONNullableImplBase<DataTypeUInt64>
{
public:
static constexpr auto name{"JSONExtractUInt"};
static Field getValue(ParsedJson::iterator & pjh)
{
if (pjh.is_integer())
return {pjh.get_integer()};
else
return getDefault();
}
};
class JSONExtractIntImpl : public JSONNullableImplBase<DataTypeInt64>
{
public:
static constexpr auto name{"JSONExtractInt"};
static Field getValue(ParsedJson::iterator & pjh)
{
if (pjh.is_integer())
return {pjh.get_integer()};
else
return getDefault();
}
};
class JSONExtractFloatImpl : public JSONNullableImplBase<DataTypeFloat64>
{
public:
static constexpr auto name{"JSONExtractFloat"};
static Field getValue(ParsedJson::iterator & pjh)
{
if (pjh.is_double())
return {pjh.get_double()};
else
return getDefault();
}
};
class JSONExtractBoolImpl : public JSONNullableImplBase<DataTypeUInt8>
{
public:
static constexpr auto name{"JSONExtractBool"};
static Field getValue(ParsedJson::iterator & pjh)
{
if (pjh.get_type() == 't')
return {1};
else if (pjh.get_type() == 'f')
return {0};
else
return getDefault();
}
};
class JSONExtractRawImpl: public JSONNullableImplBase<DataTypeString>
{
public:
static constexpr auto name {"JSONExtractRaw"};
static Field getValue(ParsedJson::iterator & pjh)
{
WriteBufferFromOwnString buf;
traverse(pjh, buf);
return {std::move(buf.str())};
}
private:
static void traverse(ParsedJson::iterator & pjh, WriteBuffer & buf)
{
switch (pjh.get_type())
{
case '{':
{
writeChar('{', buf);
if (pjh.down())
{
writeJSONString(pjh.get_string(), pjh.get_string() + pjh.get_string_length(), buf, format_settings());
writeChar(':', buf);
pjh.next();
traverse(pjh, buf);
while (pjh.next())
{
writeChar(',', buf);
writeJSONString(pjh.get_string(), pjh.get_string() + pjh.get_string_length(), buf, format_settings());
writeChar(':', buf);
pjh.next();
traverse(pjh, buf);
}
pjh.up();
}
writeChar('}', buf);
break;
}
case '[':
{
writeChar('[', buf);
if (pjh.down())
{
traverse(pjh, buf);
while (pjh.next())
{
writeChar(',', buf);
traverse(pjh, buf);
}
pjh.up();
}
writeChar(']', buf);
break;
}
case '"':
{
writeJSONString(pjh.get_string(), pjh.get_string() + pjh.get_string_length(), buf, format_settings());
break;
}
case 'l':
{
writeIntText(pjh.get_integer(), buf);
break;
}
case 'd':
{
writeFloatText(pjh.get_double(), buf);
break;
}
case 't':
{
writeCString("true", buf);
break;
}
case 'f':
{
writeCString("false", buf);
break;
}
case 'n':
{
writeCString("null", buf);
break;
}
}
}
static const FormatSettings & format_settings()
{
static const FormatSettings the_instance = []
{
FormatSettings settings;
settings.json.escape_forward_slashes = false;
return settings;
}();
return the_instance;
}
};
class JSONExtractStringImpl : public JSONNullableImplBase<DataTypeString>
{
public:
static constexpr auto name{"JSONExtractString"};
static Field getValue(ParsedJson::iterator & pjh)
{
if (pjh.is_string())
return {String{pjh.get_string()}};
else
return getDefault();
}
};
class JSONExtractKeyImpl : public JSONNullableImplBase<DataTypeString>
{
public:
static constexpr auto name{"JSONExtractKey"};
static Field getValue(ParsedJson::iterator & pjh)
{
if (pjh.get_scope_type() == '{' && pjh.prev() && pjh.is_string())
return {String{pjh.get_string()}};
else
return getDefault();
}
};
}
#else
namespace DB
{
struct JSONHasImpl { static constexpr auto name{"JSONHas"}; };
struct JSONLengthImpl { static constexpr auto name{"JSONLength"}; };
struct JSONTypeImpl { static constexpr auto name{"JSONType"}; };
struct JSONExtractImpl { static constexpr auto name{"JSONExtract"}; };
struct JSONExtractUIntImpl { static constexpr auto name{"JSONExtractUInt"}; };
struct JSONExtractIntImpl { static constexpr auto name{"JSONExtractInt"}; };
struct JSONExtractFloatImpl { static constexpr auto name{"JSONExtractFloat"}; };
struct JSONExtractBoolImpl { static constexpr auto name{"JSONExtractBool"}; };
struct JSONExtractRawImpl { static constexpr auto name {"JSONExtractRaw"}; };
struct JSONExtractStringImpl { static constexpr auto name{"JSONExtractString"}; };
struct JSONExtractKeyImpl { static constexpr auto name{"JSONExtractKey"}; };
}
#endif
namespace DB
{
@ -463,31 +11,11 @@ void registerFunctionsJSON(FunctionFactory & factory)
#if USE_SIMDJSON
if (__builtin_cpu_supports("avx2"))
{
factory.registerFunction<FunctionJSONBase<JSONHasImpl, false>>();
factory.registerFunction<FunctionJSONBase<JSONLengthImpl, false>>();
factory.registerFunction<FunctionJSONBase<JSONTypeImpl, false>>();
factory.registerFunction<FunctionJSONBase<JSONExtractImpl, true>>();
factory.registerFunction<FunctionJSONBase<JSONExtractUIntImpl, false>>();
factory.registerFunction<FunctionJSONBase<JSONExtractIntImpl, false>>();
factory.registerFunction<FunctionJSONBase<JSONExtractFloatImpl, false>>();
factory.registerFunction<FunctionJSONBase<JSONExtractBoolImpl, false>>();
factory.registerFunction<FunctionJSONBase<JSONExtractRawImpl, false>>();
factory.registerFunction<FunctionJSONBase<JSONExtractStringImpl, false>>();
factory.registerFunction<FunctionJSONBase<JSONExtractKeyImpl, false>>();
registerFunctionsJSONTemplate<SimdJSONParser>(factory);
return;
}
#endif
factory.registerFunction<FunctionJSONDummy<JSONHasImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONLengthImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONTypeImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONExtractImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONExtractUIntImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONExtractIntImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONExtractFloatImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONExtractBoolImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONExtractRawImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONExtractStringImpl>>();
factory.registerFunction<FunctionJSONDummy<JSONExtractKeyImpl>>();
registerFunctionsJSONTemplate<DummyJSONParser>(factory);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,110 @@
#pragma once
#include <Common/config.h>
#if USE_SIMDJSON
#include <common/StringRef.h>
#include <Common/Exception.h>
#include <Core/Types.h>
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wnewline-eof"
#endif
#include <simdjson/jsonparser.h>
#ifdef __clang__
#pragma clang diagnostic pop
#endif
namespace DB
{
namespace ErrorCodes
{
extern const int CANNOT_ALLOCATE_MEMORY;
}
/// This class can be used as an argument for the template class FunctionJSON.
/// It provides ability to parse JSONs using simdjson library.
struct SimdJSONParser
{
static constexpr bool need_preallocate = true;
void preallocate(size_t max_size)
{
if (!pj.allocateCapacity(max_size))
throw Exception{"Can not allocate memory for " + std::to_string(max_size) + " units when parsing JSON",
ErrorCodes::CANNOT_ALLOCATE_MEMORY};
}
bool parse(const char * data, size_t size) { return !json_parse(data, size, pj); }
using Iterator = ParsedJson::iterator;
Iterator getRoot() { return Iterator{pj}; }
static bool downToArray(Iterator & it) { return it.down(); }
static bool downToObject(Iterator & it) { return it.down() && it.next(); }
static bool downToObject(Iterator & it, StringRef & first_key)
{
if (!it.down())
return false;
first_key.data = it.get_string();
first_key.size = it.get_string_length();
return it.next();
}
static bool parentScopeIsObject(const Iterator & it) { return it.get_scope_type() == '{'; }
static bool next(Iterator & it) { return it.next(); }
static bool nextKeyValue(Iterator & it) { return it.next() && it.next(); }
static bool nextKeyValue(Iterator & it, StringRef & key)
{
if (!it.next())
return false;
key.data = it.get_string();
key.size = it.get_string_length();
return it.next();
}
static bool isInteger(const Iterator & it) { return it.is_integer(); }
static bool isFloat(const Iterator & it) { return it.is_double(); }
static bool isString(const Iterator & it) { return it.is_string(); }
static bool isArray(const Iterator & it) { return it.is_array(); }
static bool isObject(const Iterator & it) { return it.is_object(); }
static bool isBool(const Iterator & it) { return it.get_type() == 't' || it.get_type() == 'f'; }
static bool isNull(const Iterator & it) { return it.get_type() == 'n'; }
static StringRef getKey(const Iterator & it)
{
Iterator it2 = it;
it2.prev();
return StringRef{it2.get_string(), it2.get_string_length()};
}
static StringRef getString(const Iterator & it) { return StringRef{it.get_string(), it.get_string_length()}; }
static Int64 getInteger(const Iterator & it) { return it.get_integer(); }
static double getFloat(const Iterator & it) { return it.get_double(); }
static bool getBool(const Iterator & it) { return it.get_type() == 't'; }
private:
ParsedJson pj;
};
}
#endif

View File

@ -38,18 +38,28 @@ private:
working_buffer = internal_buffer;
}
static constexpr size_t initial_size = 32;
public:
WriteBufferFromVector(VectorType & vector_)
: WriteBuffer(reinterpret_cast<Position>(vector_.data()), vector_.size()), vector(vector_)
{
if (vector.empty())
{
static constexpr size_t initial_size = 32;
vector.resize(initial_size);
set(reinterpret_cast<Position>(vector.data()), vector.size());
}
}
struct AppendModeTag {};
WriteBufferFromVector(VectorType & vector_, AppendModeTag)
: WriteBuffer(nullptr, 0), vector(vector_)
{
size_t old_size = vector.size();
vector.resize(vector.capacity() < initial_size ? initial_size : vector.capacity());
set(reinterpret_cast<Position>(vector.data() + old_size), (vector.size() - old_size) * sizeof(typename VectorType::value_type));
}
void finish()
{
if (is_finished)

View File

@ -1,26 +1,52 @@
--JSONLength--
2
Object
1
1
a
b
b
a
hello
hello
3
0
--JSONHas--
1
1
0
--JSONKey--
a
b
b
a
--JSONType--
Object
Array
--JSONExtract<type>--
hello
hello
-100
200
300
('a','hello','b',[-100,200,300])
[-100,NULL,300]
['a','hello','b',NULL]
[(NULL,NULL,NULL),(NULL,NULL,NULL),(NULL,NULL,NULL),(-100,200,44)]
{"a":"hello","b":[-100,200,300],"c":{"d":[121,144]}}
1
--JSONExtract (generic)--
('hello',[-100,200,300])
('hello',[-100,200,300])
([-100,200,300],'hello')
('hello\0',0)
hello
[-100,200,300]
(-100,200,300)
[-100,0,0]
[-100,NULL,NULL]
[0,200,0]
[NULL,200,NULL]
-100
200
\N
Thursday
Friday
--JSONExtractRaw--
{"a":"hello","b":[-100,200,300]}
"hello"
[-100,200,300]
-100
{"a":"hello","b":[-100,200,300],"c":{"d":[121,144]}}
{"d":[121,144]}
[121,144]
144
{"passed":true}
{}

View File

@ -1,26 +1,58 @@
SELECT '--JSONLength--';
SELECT JSONLength('{"a": "hello", "b": [-100, 200.0, 300]}');
SELECT JSONType('{"a": "hello", "b": [-100, 200.0, 300]}');
SELECT JSONLength('{"a": "hello", "b": [-100, 200.0, 300]}', 'b');
SELECT JSONLength('{}');
SELECT '--JSONHas--';
SELECT JSONHas('{"a": "hello", "b": [-100, 200.0, 300]}', 'a');
SELECT JSONHas('{"a": "hello", "b": [-100, 200.0, 300]}', 'b');
SELECT JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', 1);
SELECT JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', 2);
SELECT JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', -1);
SELECT JSONExtractKey('{"a": "hello", "b": [-100, 200.0, 300]}', -2);
SELECT JSONExtractString('{"a": "hello", "b": [-100, 200.0, 300]}', 1);
SELECT JSONExtractString('{"a": "hello", "b": [-100, 200.0, 300]}', 'a');
SELECT JSONLength('{"a": "hello", "b": [-100, 200.0, 300]}', 'b');
SELECT JSONHas('{"a": "hello", "b": [-100, 200.0, 300]}', 'c');
SELECT '--JSONKey--';
SELECT JSONKey('{"a": "hello", "b": [-100, 200.0, 300]}', 1);
SELECT JSONKey('{"a": "hello", "b": [-100, 200.0, 300]}', 2);
SELECT JSONKey('{"a": "hello", "b": [-100, 200.0, 300]}', -1);
SELECT JSONKey('{"a": "hello", "b": [-100, 200.0, 300]}', -2);
SELECT '--JSONType--';
SELECT JSONType('{"a": "hello", "b": [-100, 200.0, 300]}');
SELECT JSONType('{"a": "hello", "b": [-100, 200.0, 300]}', 'b');
SELECT '--JSONExtract<type>--';
SELECT JSONExtractString('{"a": "hello", "b": [-100, 200.0, 300]}', 'a');
SELECT JSONExtractString('{"a": "hello", "b": [-100, 200.0, 300]}', 1);
SELECT JSONExtractInt('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 1);
SELECT JSONExtractFloat('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 2);
SELECT JSONExtractUInt('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', -1);
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Tuple(String, String, String, Array(Float64))');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 'Array(Int32)');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Array(String)');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Array(Tuple(Int16, Float32, UInt8))');
SELECT JSONExtractBool('{"passed": true}', 'passed');
SELECT '--JSONExtract (generic)--';
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Tuple(String, Array(Float64))');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Tuple(a String, b Array(Float64))');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Tuple(b Array(Float64), a String)');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'Tuple(a FixedString(6), c UInt8)');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'a', 'String');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 'Array(Float32)');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 'Tuple(Int8, Float32, UInt16)');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 'Array(Int8)');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 'Array(Nullable(Int8))');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 'Array(UInt8)');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 'Array(Nullable(UInt8))');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 1, 'Int8');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 2, 'Int32');
SELECT JSONExtract('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 4, 'Nullable(Int64)');
SELECT JSONExtract('{"day": "Thursday"}', 'day', 'Enum8(\'Sunday\' = 0, \'Monday\' = 1, \'Tuesday\' = 2, \'Wednesday\' = 3, \'Thursday\' = 4, \'Friday\' = 5, \'Saturday\' = 6)');
SELECT JSONExtract('{"day": 5}', 'day', 'Enum8(\'Sunday\' = 0, \'Monday\' = 1, \'Tuesday\' = 2, \'Wednesday\' = 3, \'Thursday\' = 4, \'Friday\' = 5, \'Saturday\' = 6)');
SELECT '--JSONExtractRaw--';
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300]}');
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300]}', 'a');
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300]}', 'b');
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300]}', 'b', 1);
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300], "c":{"d":[121,144]}}');
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300], "c":{"d":[121,144]}}', 'a');
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300], "c":{"d":[121,144]}}', 'b');
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300], "c":{"d":[121,144]}}', 'b', 1);
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300], "c":{"d":[121,144]}}', 'c');
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300], "c":{"d":[121,144]}}', 'c', 'd');
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300], "c":{"d":[121,144]}}', 'c', 'd', 2);
SELECT JSONExtractRaw('{"a": "hello", "b": [-100, 200.0, 300], "c":{"d":[121,144]}}', 'c', 'd', 3);
SELECT JSONExtractRaw('{"passed": true}');
SELECT JSONExtractRaw('{}');