mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
fix check for nondeterministic mutations
This commit is contained in:
parent
5365cc686f
commit
afd69ef833
@ -54,24 +54,33 @@ public:
|
||||
{
|
||||
ContextPtr context;
|
||||
std::optional<String> nondeterministic_function_name;
|
||||
bool subquery = false;
|
||||
};
|
||||
|
||||
static bool needChildVisit(const ASTPtr & /*node*/, const ASTPtr & child)
|
||||
static bool needChildVisit(const ASTPtr & /*node*/, const ASTPtr & /*child*/)
|
||||
{
|
||||
return child != nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void visit(const ASTPtr & node, Data & data)
|
||||
{
|
||||
if (data.nondeterministic_function_name)
|
||||
if (data.nondeterministic_function_name || data.subquery)
|
||||
return;
|
||||
|
||||
if (const auto * function = typeid_cast<const ASTFunction *>(node.get()))
|
||||
if (node->as<ASTSelectQuery>())
|
||||
{
|
||||
/// We cannot determine if subquery is deterministic or not,
|
||||
/// so we do not allow to use subqueries in mutation without allow_nondeterministic_mutations=1
|
||||
data.subquery = true;
|
||||
}
|
||||
else 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")
|
||||
{
|
||||
/// NOTE It may be an aggregate function, so get(...) may throw.
|
||||
/// However, an aggregate function can be used only in subquery and we do not go into subquery.
|
||||
const auto func = FunctionFactory::instance().get(function->name, data.context);
|
||||
if (!func->isDeterministic())
|
||||
data.nondeterministic_function_name = func->getName();
|
||||
@ -81,10 +90,11 @@ public:
|
||||
};
|
||||
|
||||
using FirstNonDeterministicFunctionFinder = InDepthNodeVisitor<FirstNonDeterministicFunctionMatcher, true>;
|
||||
using FirstNonDeterministicFunctionData = FirstNonDeterministicFunctionMatcher::Data;
|
||||
|
||||
std::optional<String> findFirstNonDeterministicFunctionName(const MutationCommand & command, ContextPtr context)
|
||||
FirstNonDeterministicFunctionData findFirstNonDeterministicFunctionName(const MutationCommand & command, ContextPtr context)
|
||||
{
|
||||
FirstNonDeterministicFunctionMatcher::Data finder_data{context, std::nullopt};
|
||||
FirstNonDeterministicFunctionMatcher::Data finder_data{context, std::nullopt, false};
|
||||
|
||||
switch (command.type)
|
||||
{
|
||||
@ -94,7 +104,7 @@ std::optional<String> findFirstNonDeterministicFunctionName(const MutationComman
|
||||
FirstNonDeterministicFunctionFinder(finder_data).visit(update_assignments_ast);
|
||||
|
||||
if (finder_data.nondeterministic_function_name)
|
||||
return finder_data.nondeterministic_function_name;
|
||||
return finder_data;
|
||||
|
||||
/// Currently UPDATE and DELETE both always have predicates so we can use fallthrough
|
||||
[[fallthrough]];
|
||||
@ -105,7 +115,7 @@ std::optional<String> findFirstNonDeterministicFunctionName(const MutationComman
|
||||
auto predicate_ast = command.predicate->clone();
|
||||
FirstNonDeterministicFunctionFinder(finder_data).visit(predicate_ast);
|
||||
|
||||
return finder_data.nondeterministic_function_name;
|
||||
return finder_data;
|
||||
}
|
||||
|
||||
default:
|
||||
@ -918,12 +928,15 @@ void MutationsInterpreter::validate()
|
||||
{
|
||||
for (const auto & command : commands)
|
||||
{
|
||||
const auto nondeterministic_func_name = findFirstNonDeterministicFunctionName(command, context);
|
||||
if (nondeterministic_func_name)
|
||||
throw Exception(
|
||||
"ALTER UPDATE/ALTER DELETE statements must use only deterministic functions! "
|
||||
"Function '" + *nondeterministic_func_name + "' is non-deterministic",
|
||||
ErrorCodes::BAD_ARGUMENTS);
|
||||
const auto nondeterministic_func_data = findFirstNonDeterministicFunctionName(command, context);
|
||||
if (nondeterministic_func_data.subquery)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "ALTER UPDATE/ALTER DELETE statement with subquery may be nondeterministic, "
|
||||
"see allow_nondeterministic_mutations setting");
|
||||
|
||||
if (nondeterministic_func_data.nondeterministic_function_name)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"ALTER UPDATE/ALTER DELETE statements must use only deterministic functions. "
|
||||
"Function '{}' is non-deterministic", *nondeterministic_func_data.nondeterministic_function_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ MergeTree
|
||||
2
|
||||
0
|
||||
50 6225 0
|
||||
0
|
||||
50 6225 1900
|
||||
ReplicatedMergeTree
|
||||
1
|
||||
@ -12,15 +11,13 @@ ReplicatedMergeTree
|
||||
2
|
||||
0
|
||||
50 6225 0
|
||||
2
|
||||
50 6225 0
|
||||
50 6225 1900
|
||||
Memory
|
||||
1
|
||||
2
|
||||
2
|
||||
0
|
||||
50 6225 0
|
||||
0
|
||||
50 6225 1900
|
||||
Join
|
||||
1
|
||||
@ -28,5 +25,4 @@ Join
|
||||
2
|
||||
0
|
||||
50 6225 0
|
||||
0
|
||||
50 6225 0
|
||||
|
@ -16,17 +16,29 @@ do
|
||||
$CLICKHOUSE_CLIENT -q "insert into t values (1)"
|
||||
$CLICKHOUSE_CLIENT -q "insert into t values (2)"
|
||||
$CLICKHOUSE_CLIENT -q "select * from t order by n"
|
||||
$CLICKHOUSE_CLIENT --mutations_sync=1 -q "alter table t delete where n global in (select * from (select * from t where n global in (1::Int32)))"
|
||||
$CLICKHOUSE_CLIENT --allow_nondeterministic_mutations=1 --mutations_sync=1 -q "alter table t
|
||||
delete where n global in (select * from (select * from t where n global in (1::Int32)))"
|
||||
$CLICKHOUSE_CLIENT -q "select * from t order by n"
|
||||
$CLICKHOUSE_CLIENT --mutations_sync=1 -q "alter table t delete where n global in (select t1.n from t as t1 full join t as t2 on t1.n=t2.n where t1.n global in (select 2::Int32))"
|
||||
$CLICKHOUSE_CLIENT --allow_nondeterministic_mutations=1 --mutations_sync=1 -q "alter table t
|
||||
delete where n global in (select t1.n from t as t1 full join t as t2 on t1.n=t2.n where t1.n global in (select 2::Int32))"
|
||||
$CLICKHOUSE_CLIENT -q "select count() from t"
|
||||
$CLICKHOUSE_CLIENT -q "drop table t"
|
||||
|
||||
$CLICKHOUSE_CLIENT -q "drop table if exists test"
|
||||
$CLICKHOUSE_CLIENT -q "CREATE TABLE test ENGINE=$engine AS SELECT number + 100 AS n, 0 AS test FROM numbers(50)"
|
||||
$CLICKHOUSE_CLIENT -q "select count(), sum(n), sum(test) from test"
|
||||
# FIXME it's not clear if the following query should fail or not
|
||||
$CLICKHOUSE_CLIENT --mutations_sync=1 -q "ALTER TABLE test UPDATE test = (SELECT groupArray(id) FROM t1 GROUP BY 1)[n - 99] WHERE 1" 2>&1| grep -c "Unknown function"
|
||||
if [[ $engine == *"ReplicatedMergeTree"* ]]; then
|
||||
$CLICKHOUSE_CLIENT -q "ALTER TABLE test
|
||||
UPDATE test = (SELECT groupArray(id) FROM t1 GROUP BY 1)[n - 99] WHERE 1" 2>&1| grep -Fa "DB::Exception: " | grep -Fv "statement with subquery may be nondeterministic"
|
||||
$CLICKHOUSE_CLIENT --allow_nondeterministic_mutations=1 --mutations_sync=1 -q "ALTER TABLE test
|
||||
UPDATE test = (SELECT groupArray(id) FROM t1 GROUP BY 1)[n - 99] WHERE 1"
|
||||
elif [[ $engine == *"Join"* ]]; then
|
||||
$CLICKHOUSE_CLIENT -q "ALTER TABLE test
|
||||
UPDATE test = (SELECT groupArray(id) FROM t1 GROUP BY 1)[n - 99] WHERE 1" 2>&1| grep -Fa "DB::Exception: " | grep -Fv "Table engine Join supports only DELETE mutations"
|
||||
else
|
||||
$CLICKHOUSE_CLIENT --mutations_sync=1 -q "ALTER TABLE test
|
||||
UPDATE test = (SELECT groupArray(id) FROM t1 GROUP BY 1)[n - 99] WHERE 1"
|
||||
fi
|
||||
$CLICKHOUSE_CLIENT -q "select count(), sum(n), sum(test) from test"
|
||||
$CLICKHOUSE_CLIENT -q "drop table test"
|
||||
done
|
||||
|
Loading…
Reference in New Issue
Block a user