Merge pull request #10237 from ClickHouse/akz/mutations-to-correctly-handle-lambdas

ALTER UPDATE/DELETE on Replicated* storages: Fixed "Unknown function lambda." error
This commit is contained in:
alexey-milovidov 2020-04-14 01:32:38 +03:00 committed by GitHub
commit b8be585595
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 38 additions and 18 deletions

View File

@ -36,34 +36,46 @@ namespace ErrorCodes
namespace
{
struct FirstNonDeterministicFuncData
/// Helps to detect situations, where non-deterministic functions may be used in mutations of Replicated*MergeTree.
class FirstNonDeterministicFuncMatcher
{
using TypeToVisit = ASTFunction;
explicit FirstNonDeterministicFuncData(const Context & context_)
: context{context_}
{}
const Context & context;
std::optional<String> nondeterministic_function_name;
void visit(ASTFunction & function, ASTPtr &)
public:
struct Data
{
if (nondeterministic_function_name)
const Context & context;
std::optional<String> nondeterministic_function_name;
};
public:
static bool needChildVisit(const ASTPtr & /*node*/, const ASTPtr & child)
{
return child != nullptr;
}
static void visit(const ASTPtr & node, Data & data)
{
if (data.nondeterministic_function_name)
return;
const auto func = FunctionFactory::instance().get(function.name, context);
if (!func->isDeterministic())
nondeterministic_function_name = func->getName();
if (const auto * function = typeid_cast<const ASTFunction *>(node.get()))
{
/// Property of being deterministic for lambda expression is completely determined
/// by the contents of its definition, so we just proceed to it.
if (function->name != "lambda")
{
const auto func = FunctionFactory::instance().get(function->name, data.context);
if (!func->isDeterministic())
data.nondeterministic_function_name = func->getName();
}
}
}
};
using FirstNonDeterministicFuncFinder =
InDepthNodeVisitor<OneTypeMatcher<FirstNonDeterministicFuncData>, true>;
using FirstNonDeterministicFuncFinder = InDepthNodeVisitor<FirstNonDeterministicFuncMatcher, true>;
std::optional<String> findFirstNonDeterministicFuncName(const MutationCommand & command, const Context & context)
{
FirstNonDeterministicFuncData finder_data(context);
FirstNonDeterministicFuncMatcher::Data finder_data{context, std::nullopt};
switch (command.type)
{

View File

@ -43,6 +43,12 @@ ${CLICKHOUSE_CLIENT} --query "ALTER TABLE $R1 DELETE WHERE ignore(rand())" 2>&1
${CLICKHOUSE_CLIENT} --query "ALTER TABLE $R1 UPDATE y = y + rand() % 1 WHERE not ignore()" 2>&1 \
| fgrep -q "must use only deterministic functions" && echo 'OK' || echo 'FAIL'
${CLICKHOUSE_CLIENT} --query "ALTER TABLE $R1 UPDATE y = x + arrayCount(x -> (x + y) % 2, range(y)) WHERE not ignore()" 2>&1 > /dev/null \
&& echo 'OK' || echo 'FAIL'
${CLICKHOUSE_CLIENT} --query "ALTER TABLE $R1 UPDATE y = x + arrayCount(x -> (rand() + x) % 2, range(y)) WHERE not ignore()" 2>&1 \
| fgrep -q "must use only deterministic functions" && echo 'OK' || echo 'FAIL'
# For regular tables we do not enforce deterministic functions
${CLICKHOUSE_CLIENT} --query "ALTER TABLE $T1 DELETE WHERE rand() = 0" 2>&1 > /dev/null \