ClickHouse/dbms/src/Functions/FunctionsJSON.h

278 lines
7.3 KiB
C++
Raw Normal View History

2019-03-14 02:55:04 +00:00
#pragma once
#include <Columns/ColumnConst.h>
2019-03-14 08:07:25 +00:00
#include <Columns/ColumnString.h>
2019-03-14 02:55:04 +00:00
#include <Common/typeid_cast.h>
2019-03-14 05:48:29 +00:00
#include <DataTypes/DataTypeFactory.h>
2019-03-14 02:55:04 +00:00
#include <Functions/IFunction.h>
#include <ext/range.h>
#include <simdjson/jsonparser.h>
namespace DB
{
namespace ErrorCodes
{
2019-03-14 08:07:25 +00:00
extern const int CANNOT_ALLOCATE_MEMORY;
2019-03-14 06:19:21 +00:00
extern const int ILLEGAL_COLUMN;
2019-03-14 02:55:04 +00:00
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
}
template <typename Impl, bool ExtraArg>
class FunctionJSONBase : public IFunction
{
private:
enum class Action
{
key = 1,
index = 2,
};
mutable std::vector<Action> actions;
mutable DataTypePtr virtual_type;
bool tryMove(
ParsedJson::iterator & pjh,
Action action,
const Field & accessor
)
{
switch (action)
{
case Action::key:
if (
!pjh.is_object()
|| !pjh.move_to_key(accessor.get<String>().data())
)
return false;
break;
case Action::index:
if (
!pjh.is_object_or_array()
|| !pjh.down()
)
return false;
int steps = accessor.get<Int64>();
if (steps > 0)
steps -= 1;
else if (steps < 0)
{
steps += 1;
ParsedJson::iterator pjh1 {
pjh
};
while (pjh1.next())
steps += 1;
}
else
return false;
for (const auto i : ext::range(0, steps))
{
(void) i;
if (!pjh.next())
return false;
}
break;
}
return true;
}
public:
static constexpr auto name = Impl::name;
static FunctionPtr create(const Context &)
{
return std::make_shared<FunctionJSONBase>();
}
String getName() const override
{
return Impl::name;
}
bool isVariadic() const override
{
return true;
}
size_t getNumberOfArguments() const override
{
return 0;
}
bool useDefaultImplementationForConstants() const override
{
return true;
}
2019-03-14 05:48:29 +00:00
DataTypePtr getReturnTypeImpl(
const ColumnsWithTypeAndName & arguments
) const override
2019-03-14 02:55:04 +00:00
{
if constexpr (ExtraArg)
{
if (arguments.size() < 2)
throw Exception {
"Function " + getName() + " requires at least two arguments",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH
};
2019-03-14 08:07:25 +00:00
auto col_type_const = typeid_cast<const ColumnConst *>(
arguments[1].column.get()
);
2019-03-14 05:48:29 +00:00
2019-03-14 06:19:21 +00:00
if (!col_type_const)
throw Exception {
2019-03-14 08:07:25 +00:00
"Illegal non-const column " + arguments[1].column->getName()
+ " of argument of function " + getName(),
2019-03-14 06:19:21 +00:00
ErrorCodes::ILLEGAL_COLUMN
};
2019-03-14 05:48:29 +00:00
virtual_type = DataTypeFactory::instance().get(
col_type_const->getValue<String>()
);
2019-03-14 02:55:04 +00:00
}
else
{
if (arguments.size() < 1)
throw Exception {
"Function " + getName() + " requires at least one arguments",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH
};
}
2019-03-14 05:48:29 +00:00
if (!isString(arguments[0].type))
2019-03-14 02:55:04 +00:00
throw Exception {
2019-03-14 05:48:29 +00:00
"Illegal type " + arguments[0].type->getName()
2019-03-14 02:55:04 +00:00
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT
};
actions.reserve(arguments.size() - 1 - ExtraArg);
for (const auto i : ext::range(1 + ExtraArg, arguments.size()))
{
2019-03-14 05:48:29 +00:00
if (isString(arguments[i].type))
2019-03-14 02:55:04 +00:00
actions.push_back(Action::key);
2019-03-14 05:48:29 +00:00
else if (isInteger(arguments[i].type))
2019-03-14 02:55:04 +00:00
actions.push_back(Action::index);
else
throw Exception {
2019-03-14 05:48:29 +00:00
"Illegal type " + arguments[i].type->getName()
2019-03-14 02:55:04 +00:00
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT
};
}
if constexpr (ExtraArg)
return Impl::getType(virtual_type);
else
return Impl::getType();
}
void executeImpl(
Block & block,
const ColumnNumbers & arguments,
size_t result_pos,
size_t input_rows_count
) override
{
MutableColumnPtr to {
block.getByPosition(result_pos).type->createColumn()
};
to->reserve(input_rows_count);
const ColumnPtr & arg_json = block.getByPosition(arguments[0]).column;
2019-03-14 08:07:25 +00:00
auto col_json_const = typeid_cast<const ColumnConst *>(
arg_json.get()
);
auto col_json_string = typeid_cast<const ColumnString *>(
col_json_const
? col_json_const->getDataColumnPtr().get()
: arg_json.get()
);
if (!col_json_string)
throw Exception {
"Illegal column " + arg_json->getName()
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN
};
const ColumnString::Chars & chars = col_json_string->getChars();
const ColumnString::Offsets & offsets = col_json_string->getOffsets();
size_t max_size = 1;
2019-03-14 02:55:04 +00:00
for (const auto i : ext::range(0, input_rows_count))
2019-03-14 08:07:25 +00:00
if (max_size < offsets[i] - offsets[i - 1] - 1)
max_size = offsets[i] - offsets[i - 1] - 1;
ParsedJson pj;
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
2019-03-14 02:55:04 +00:00
};
2019-03-14 08:07:25 +00:00
for (const auto i : ext::range(0, input_rows_count))
{
bool ok = json_parse(
&chars[offsets[i - 1]],
offsets[i] - offsets[i - 1] - 1,
pj
) == 0;
2019-03-14 02:55:04 +00:00
ParsedJson::iterator pjh {
pj
};
for (const auto j : ext::range(0, actions.size()))
{
2019-03-14 08:07:25 +00:00
if (!ok)
break;
2019-03-14 02:55:04 +00:00
ok = tryMove(
pjh,
actions[j],
(*block.getByPosition(arguments[j + 1 + ExtraArg]).column)[i]
);
}
if (ok)
{
if constexpr (ExtraArg)
to->insert(Impl::getValue(pjh, virtual_type));
else
to->insert(Impl::getValue(pjh));
}
else
{
if constexpr (ExtraArg)
to->insert(Impl::getDefault(virtual_type));
else
to->insert(Impl::getDefault());
}
}
block.getByPosition(result_pos).column = std::move(to);
}
};
}