mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 07:31:57 +00:00
Check for non-deterministic functions in keys, including constant expressions
This commit is contained in:
parent
8f1d7e67ca
commit
ff5c433f10
@ -113,7 +113,8 @@ public:
|
||||
|
||||
virtual ~IFunctionBase() = default;
|
||||
|
||||
virtual ColumnPtr execute(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count, bool dry_run = false) const
|
||||
virtual ColumnPtr execute(
|
||||
const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count, bool dry_run = false) const
|
||||
{
|
||||
return prepare(arguments)->execute(arguments, result_type, input_rows_count, dry_run);
|
||||
}
|
||||
@ -161,7 +162,8 @@ public:
|
||||
* Arguments are passed without modifications, useDefaultImplementationForNulls, useDefaultImplementationForConstants,
|
||||
* useDefaultImplementationForLowCardinality are not applied.
|
||||
*/
|
||||
virtual ColumnPtr getConstantResultForNonConstArguments(const ColumnsWithTypeAndName & /* arguments */, const DataTypePtr & /* result_type */) const { return nullptr; }
|
||||
virtual ColumnPtr getConstantResultForNonConstArguments(
|
||||
const ColumnsWithTypeAndName & /* arguments */, const DataTypePtr & /* result_type */) const { return nullptr; }
|
||||
|
||||
/** Function is called "injective" if it returns different result for different values of arguments.
|
||||
* Example: hex, negate, tuple...
|
||||
|
@ -26,6 +26,7 @@ namespace ErrorCodes
|
||||
extern const int THERE_IS_NO_COLUMN;
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int NOT_FOUND_COLUMN_IN_BLOCK;
|
||||
extern const int BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
const char * ActionsDAG::typeToString(ActionsDAG::ActionType type)
|
||||
@ -203,6 +204,14 @@ const ActionsDAG::Node & ActionsDAG::addFunction(
|
||||
node.result_type = node.function_base->getResultType();
|
||||
node.function = node.function_base->prepare(arguments);
|
||||
|
||||
std::cerr << node.function_base->getName() << ": " << node.function_base->isDeterministic() << "\n";
|
||||
|
||||
if (is_deterministic && !node.function_base->isDeterministic())
|
||||
{
|
||||
is_deterministic = false;
|
||||
non_deterministic_function = node.function_base->getName();
|
||||
}
|
||||
|
||||
/// If all arguments are constants, and function is suitable to be executed in 'prepare' stage - execute function.
|
||||
if (node.function_base->isSuitableForConstantFolding())
|
||||
{
|
||||
@ -981,6 +990,25 @@ bool ActionsDAG::trivial() const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ActionsDAG::isDeterministic() const
|
||||
{
|
||||
std::cerr << "isDeterministic: " << is_deterministic << "\n";
|
||||
|
||||
/// We cannot calculate it on the fly as above because non-deterministic
|
||||
/// but isDeterministicInScopeOfQuery/isSuitableForConstantFolding
|
||||
/// functions can be already constant folded.
|
||||
return is_deterministic;
|
||||
}
|
||||
|
||||
void ActionsDAG::assertDeterministic() const
|
||||
{
|
||||
std::cerr << "isDeterministic: " << is_deterministic << "\n";
|
||||
|
||||
if (!is_deterministic)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Expression must be deterministic but it contains non-deterministic function {}", non_deterministic_function);
|
||||
}
|
||||
|
||||
void ActionsDAG::addMaterializingOutputActions()
|
||||
{
|
||||
for (auto & node : index)
|
||||
|
@ -103,6 +103,9 @@ private:
|
||||
bool project_input = false;
|
||||
bool projected_output = false;
|
||||
|
||||
bool is_deterministic = true;
|
||||
String non_deterministic_function; /// For exception message.
|
||||
|
||||
public:
|
||||
ActionsDAG() = default;
|
||||
ActionsDAG(ActionsDAG &&) = default;
|
||||
@ -175,6 +178,8 @@ public:
|
||||
bool hasArrayJoin() const;
|
||||
bool hasStatefulFunctions() const;
|
||||
bool trivial() const; /// If actions has no functions or array join.
|
||||
bool isDeterministic() const; /// All functions are 'isDeterministic'.
|
||||
void assertDeterministic() const; /// Throw if not isDeterministic.
|
||||
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
void compileExpressions(size_t min_count_to_compile_expression);
|
||||
|
@ -531,11 +531,17 @@ Names ExpressionActions::getRequiredColumns() const
|
||||
|
||||
bool ExpressionActions::hasArrayJoin() const
|
||||
{
|
||||
for (const auto & action : actions)
|
||||
if (action.node->type == ActionsDAG::ActionType::ARRAY_JOIN)
|
||||
return true;
|
||||
return getActionsDAG().hasArrayJoin();
|
||||
}
|
||||
|
||||
return false;
|
||||
bool ExpressionActions::isDeterministic() const
|
||||
{
|
||||
return getActionsDAG().isDeterministic();
|
||||
}
|
||||
|
||||
void ExpressionActions::assertDeterministic() const
|
||||
{
|
||||
getActionsDAG().assertDeterministic();
|
||||
}
|
||||
|
||||
|
||||
|
@ -103,6 +103,8 @@ public:
|
||||
void execute(Block & block, bool dry_run = false) const;
|
||||
|
||||
bool hasArrayJoin() const;
|
||||
bool isDeterministic() const;
|
||||
void assertDeterministic() const;
|
||||
|
||||
/// Obtain a sample block that contains the names and types of result columns.
|
||||
const Block & getSampleBlock() const { return sample_block; }
|
||||
|
@ -270,19 +270,17 @@ StoragePolicyPtr MergeTreeData::getStoragePolicy() const
|
||||
|
||||
static void checkKeyExpression(const ExpressionActions & expr, const Block & sample_block, const String & key_name, bool allow_nullable_key)
|
||||
{
|
||||
for (const auto & action : expr.getActions())
|
||||
{
|
||||
if (action.node->type == ActionsDAG::ActionType::ARRAY_JOIN)
|
||||
throw Exception(key_name + " key cannot contain array joins", ErrorCodes::ILLEGAL_COLUMN);
|
||||
if (expr.hasArrayJoin())
|
||||
throw Exception(key_name + " key cannot contain array joins", ErrorCodes::ILLEGAL_COLUMN);
|
||||
|
||||
if (action.node->type == ActionsDAG::ActionType::FUNCTION)
|
||||
{
|
||||
IFunctionBase & func = *action.node->function_base;
|
||||
if (!func.isDeterministic())
|
||||
throw Exception(key_name + " key cannot contain non-deterministic functions, "
|
||||
"but contains function " + func.getName(),
|
||||
ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
try
|
||||
{
|
||||
expr.assertDeterministic();
|
||||
}
|
||||
catch (Exception & e)
|
||||
{
|
||||
e.addMessage(fmt::format("for {} key", key_name));
|
||||
throw;
|
||||
}
|
||||
|
||||
for (const ColumnWithTypeAndName & element : sample_block)
|
||||
|
Loading…
Reference in New Issue
Block a user