mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-27 01:51:59 +00:00
Merge pull request #55604 from ClickHouse/evillique-partition-parameters
Implement query paramenters support in partition expression
This commit is contained in:
commit
697c1e3bb0
@ -172,7 +172,7 @@ BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter)
|
||||
|
||||
if (!partition_commands.empty())
|
||||
{
|
||||
table->checkAlterPartitionIsPossible(partition_commands, metadata_snapshot, getContext()->getSettingsRef());
|
||||
table->checkAlterPartitionIsPossible(partition_commands, metadata_snapshot, getContext()->getSettingsRef(), getContext());
|
||||
auto partition_commands_pipe = table->alterPartition(metadata_snapshot, partition_commands, getContext());
|
||||
if (!partition_commands_pipe.empty())
|
||||
res.pipeline = QueryPipeline(std::move(partition_commands_pipe));
|
||||
|
@ -5,12 +5,39 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
void ASTPartition::setPartitionID(const ASTPtr & ast)
|
||||
{
|
||||
if (children.empty())
|
||||
{
|
||||
children.push_back(ast);
|
||||
id = children[0].get();
|
||||
}
|
||||
else
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot have multiple children for partition AST");
|
||||
}
|
||||
void ASTPartition::setPartitionValue(const ASTPtr & ast)
|
||||
{
|
||||
if (children.empty())
|
||||
{
|
||||
children.push_back(ast);
|
||||
value = children[0].get();
|
||||
}
|
||||
else
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot have multiple children for partition AST");
|
||||
}
|
||||
|
||||
|
||||
String ASTPartition::getID(char delim) const
|
||||
{
|
||||
if (value)
|
||||
return "Partition";
|
||||
else
|
||||
return "Partition_ID" + (delim + id);
|
||||
return "Partition_ID" + (delim + id->getID());
|
||||
}
|
||||
|
||||
ASTPtr ASTPartition::clone() const
|
||||
@ -20,8 +47,14 @@ ASTPtr ASTPartition::clone() const
|
||||
|
||||
if (value)
|
||||
{
|
||||
res->value = value->clone();
|
||||
res->children.push_back(res->value);
|
||||
res->children.push_back(children[0]->clone());
|
||||
res->value = res->children[0].get();
|
||||
}
|
||||
|
||||
if (id)
|
||||
{
|
||||
res->children.push_back(children[0]->clone());
|
||||
res->id = res->children[0].get();
|
||||
}
|
||||
|
||||
return res;
|
||||
@ -33,18 +66,14 @@ void ASTPartition::formatImpl(const FormatSettings & settings, FormatState & sta
|
||||
{
|
||||
value->formatImpl(settings, state, frame);
|
||||
}
|
||||
else if (all)
|
||||
{
|
||||
settings.ostr << "ALL";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (all)
|
||||
settings.ostr << "ALL";
|
||||
else
|
||||
{
|
||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << "ID " << (settings.hilite ? hilite_none : "");
|
||||
WriteBufferFromOwnString id_buf;
|
||||
writeQuoted(id, id_buf);
|
||||
settings.ostr << id_buf.str();
|
||||
}
|
||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << "ID " << (settings.hilite ? hilite_none : "");
|
||||
id->formatImpl(settings, state, frame);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Parsers/IAST.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -10,15 +10,24 @@ namespace DB
|
||||
class ASTPartition : public IAST
|
||||
{
|
||||
public:
|
||||
ASTPtr value;
|
||||
size_t fields_count = 0;
|
||||
IAST * value{nullptr};
|
||||
std::optional<size_t> fields_count;
|
||||
|
||||
String id;
|
||||
IAST * id{nullptr};
|
||||
bool all = false;
|
||||
|
||||
String getID(char) const override;
|
||||
ASTPtr clone() const override;
|
||||
|
||||
void setPartitionID(const ASTPtr & ast);
|
||||
void setPartitionValue(const ASTPtr & ast);
|
||||
|
||||
void forEachPointerToChild(std::function<void(void **)> f) override
|
||||
{
|
||||
f(reinterpret_cast<void **>(&value));
|
||||
f(reinterpret_cast<void **>(&id));
|
||||
}
|
||||
|
||||
protected:
|
||||
void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override;
|
||||
};
|
||||
|
@ -173,7 +173,7 @@ protected:
|
||||
class ParserExpression : public IParserBase
|
||||
{
|
||||
public:
|
||||
ParserExpression(bool allow_trailing_commas_ = false) : allow_trailing_commas(allow_trailing_commas_) {}
|
||||
explicit ParserExpression(bool allow_trailing_commas_ = false) : allow_trailing_commas(allow_trailing_commas_) {}
|
||||
|
||||
protected:
|
||||
const char * getName() const override { return "lambda expression"; }
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Parsers/ASTQueryParameter.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -16,19 +17,25 @@ bool ParserPartition::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
ParserKeyword s_id("ID");
|
||||
ParserKeyword s_all("ALL");
|
||||
ParserStringLiteral parser_string_literal;
|
||||
ParserSubstitution parser_substitution;
|
||||
ParserLiteral literal_parser;
|
||||
ParserTupleOfLiterals tuple_of_literals;
|
||||
ParserExpression parser_expr;
|
||||
|
||||
Pos begin = pos;
|
||||
|
||||
auto partition = std::make_shared<ASTPartition>();
|
||||
|
||||
if (s_id.ignore(pos, expected))
|
||||
{
|
||||
ASTPtr partition_id;
|
||||
if (!parser_string_literal.parse(pos, partition_id, expected))
|
||||
if (!parser_string_literal.parse(pos, partition_id, expected) && !parser_substitution.parse(pos, partition_id, expected))
|
||||
return false;
|
||||
|
||||
partition->id = partition_id->as<ASTLiteral &>().value.get<String>();
|
||||
if (auto * partition_id_literal = partition_id->as<ASTLiteral>(); partition_id_literal != nullptr)
|
||||
partition->setPartitionID(partition_id);
|
||||
else if (auto * partition_id_query_parameter = partition_id->as<ASTQueryParameter>(); partition_id_query_parameter != nullptr)
|
||||
partition->setPartitionID(partition_id);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (s_all.ignore(pos, expected))
|
||||
{
|
||||
@ -37,27 +44,12 @@ bool ParserPartition::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
else
|
||||
{
|
||||
ASTPtr value;
|
||||
if (!parser_expr.parse(pos, value, expected))
|
||||
return false;
|
||||
|
||||
size_t fields_count;
|
||||
|
||||
const auto * tuple_ast = value->as<ASTFunction>();
|
||||
bool surrounded_by_parens = false;
|
||||
if (tuple_ast && tuple_ast->name == "tuple")
|
||||
{
|
||||
surrounded_by_parens = true;
|
||||
const auto * arguments_ast = tuple_ast->arguments->as<ASTExpressionList>();
|
||||
if (arguments_ast)
|
||||
fields_count = arguments_ast->children.size();
|
||||
else
|
||||
fields_count = 0;
|
||||
}
|
||||
else if (const auto * literal = value->as<ASTLiteral>())
|
||||
std::optional<size_t> fields_count;
|
||||
if (literal_parser.parse(pos, value, expected) || tuple_of_literals.parse(pos, value, expected))
|
||||
{
|
||||
auto * literal = value->as<ASTLiteral>();
|
||||
if (literal->value.getType() == Field::Types::Tuple)
|
||||
{
|
||||
surrounded_by_parens = true;
|
||||
fields_count = literal->value.get<const Tuple &>().size();
|
||||
}
|
||||
else
|
||||
@ -65,27 +57,31 @@ bool ParserPartition::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
fields_count = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
if (surrounded_by_parens)
|
||||
else if (parser_substitution.parse(pos, value, expected))
|
||||
{
|
||||
Pos left_paren = begin;
|
||||
Pos right_paren = pos;
|
||||
|
||||
while (left_paren != right_paren && left_paren->type != TokenType::OpeningRoundBracket)
|
||||
++left_paren;
|
||||
if (left_paren->type != TokenType::OpeningRoundBracket)
|
||||
return false;
|
||||
|
||||
while (right_paren != left_paren && right_paren->type != TokenType::ClosingRoundBracket)
|
||||
--right_paren;
|
||||
if (right_paren->type != TokenType::ClosingRoundBracket)
|
||||
/// It can be tuple substitution
|
||||
fields_count = std::nullopt;
|
||||
}
|
||||
else if (parser_expr.parse(pos, value, expected))
|
||||
{
|
||||
const auto * tuple_ast = value->as<ASTFunction>();
|
||||
if (tuple_ast && tuple_ast->name == "tuple")
|
||||
{
|
||||
const auto * arguments_ast = tuple_ast->arguments->as<ASTExpressionList>();
|
||||
if (arguments_ast)
|
||||
fields_count = arguments_ast->children.size();
|
||||
else
|
||||
fields_count = 0;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
partition->value = value;
|
||||
partition->children.push_back(value);
|
||||
partition->setPartitionValue(value);
|
||||
partition->fields_count = fields_count;
|
||||
}
|
||||
|
||||
|
@ -211,7 +211,10 @@ void IStorage::checkMutationIsPossible(const MutationCommands & /*commands*/, co
|
||||
}
|
||||
|
||||
void IStorage::checkAlterPartitionIsPossible(
|
||||
const PartitionCommands & /*commands*/, const StorageMetadataPtr & /*metadata_snapshot*/, const Settings & /*settings*/) const
|
||||
const PartitionCommands & /*commands*/,
|
||||
const StorageMetadataPtr & /*metadata_snapshot*/,
|
||||
const Settings & /*settings*/,
|
||||
ContextPtr /*context*/) const
|
||||
{
|
||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Table engine {} doesn't support partitioning", getName());
|
||||
}
|
||||
|
@ -495,7 +495,11 @@ public:
|
||||
ContextPtr /* context */);
|
||||
|
||||
/// Checks that partition commands can be applied to storage.
|
||||
virtual void checkAlterPartitionIsPossible(const PartitionCommands & commands, const StorageMetadataPtr & metadata_snapshot, const Settings & settings) const;
|
||||
virtual void checkAlterPartitionIsPossible(
|
||||
const PartitionCommands & commands,
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
const Settings & settings,
|
||||
ContextPtr context) const;
|
||||
|
||||
/** Perform any background work. For example, combining parts in a MergeTree type table.
|
||||
* Returns whether any work has been done.
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Interpreters/convertFieldToType.h>
|
||||
#include <Interpreters/evaluateConstantExpression.h>
|
||||
#include <Interpreters/ReplaceQueryParameterVisitor.h>
|
||||
#include <Interpreters/ExpressionAnalyzer.h>
|
||||
#include <Interpreters/inplaceBlockConversions.h>
|
||||
#include <Interpreters/InterpreterSelectQuery.h>
|
||||
@ -52,6 +53,7 @@
|
||||
#include <IO/SharedThreadPools.h>
|
||||
#include <Parsers/ASTExpressionList.h>
|
||||
#include <Parsers/ASTIndexDeclaration.h>
|
||||
#include <Parsers/ASTHelpers.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
#include <Parsers/ASTNameTypePair.h>
|
||||
@ -62,6 +64,7 @@
|
||||
#include <Parsers/parseQuery.h>
|
||||
#include <Parsers/queryToString.h>
|
||||
#include <Parsers/ASTAlterQuery.h>
|
||||
#include <Parsers/ASTQueryParameter.h>
|
||||
#include <Processors/Formats/IInputFormat.h>
|
||||
#include <Processors/QueryPlan/QueryIdHolder.h>
|
||||
#include <Processors/QueryPlan/ReadFromMergeTree.h>
|
||||
@ -2967,7 +2970,7 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, Context
|
||||
/// Just validate partition expression
|
||||
if (command.partition)
|
||||
{
|
||||
getPartitionIDFromQuery(command.partition, getContext());
|
||||
getPartitionIDFromQuery(command.partition, local_context);
|
||||
}
|
||||
|
||||
if (command.column_name == merging_params.version_column)
|
||||
@ -4635,7 +4638,7 @@ void MergeTreeData::removePartContributionToColumnAndSecondaryIndexSizes(const D
|
||||
}
|
||||
|
||||
void MergeTreeData::checkAlterPartitionIsPossible(
|
||||
const PartitionCommands & commands, const StorageMetadataPtr & /*metadata_snapshot*/, const Settings & settings) const
|
||||
const PartitionCommands & commands, const StorageMetadataPtr & /*metadata_snapshot*/, const Settings & settings, ContextPtr local_context) const
|
||||
{
|
||||
for (const auto & command : commands)
|
||||
{
|
||||
@ -4663,7 +4666,7 @@ void MergeTreeData::checkAlterPartitionIsPossible(
|
||||
throw DB::Exception(ErrorCodes::SUPPORT_IS_DISABLED, "Only support DROP/DETACH PARTITION ALL currently");
|
||||
}
|
||||
else
|
||||
getPartitionIDFromQuery(command.partition, getContext());
|
||||
getPartitionIDFromQuery(command.partition, local_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5342,8 +5345,71 @@ String MergeTreeData::getPartitionIDFromQuery(const ASTPtr & ast, ContextPtr loc
|
||||
|
||||
if (!partition_ast.value)
|
||||
{
|
||||
MergeTreePartInfo::validatePartitionID(partition_ast.id, format_version);
|
||||
return partition_ast.id;
|
||||
MergeTreePartInfo::validatePartitionID(partition_ast.id->clone(), format_version);
|
||||
return partition_ast.id->as<ASTLiteral>()->value.safeGet<String>();
|
||||
}
|
||||
size_t partition_ast_fields_count = 0;
|
||||
ASTPtr partition_value_ast = partition_ast.value->clone();
|
||||
if (!partition_ast.fields_count.has_value())
|
||||
{
|
||||
if (partition_value_ast->as<ASTLiteral>())
|
||||
{
|
||||
partition_ast_fields_count = 1;
|
||||
}
|
||||
else if (const auto * tuple_ast = partition_value_ast->as<ASTFunction>())
|
||||
{
|
||||
if (tuple_ast->name != "tuple")
|
||||
{
|
||||
if (isFunctionCast(tuple_ast))
|
||||
{
|
||||
if (tuple_ast->arguments->as<ASTExpressionList>()->children.empty())
|
||||
{
|
||||
throw Exception(
|
||||
ErrorCodes::INVALID_PARTITION_VALUE, "Expected tuple for complex partition key, got {}", tuple_ast->name);
|
||||
}
|
||||
auto first_arg = tuple_ast->arguments->as<ASTExpressionList>()->children.at(0);
|
||||
if (const auto * inner_tuple = first_arg->as<ASTFunction>(); inner_tuple && inner_tuple->name == "tuple")
|
||||
{
|
||||
const auto * arguments_ast = tuple_ast->arguments->as<ASTExpressionList>();
|
||||
if (arguments_ast)
|
||||
partition_ast_fields_count = arguments_ast->children.size();
|
||||
else
|
||||
partition_ast_fields_count = 0;
|
||||
}
|
||||
else if (const auto * inner_literal_tuple = first_arg->as<ASTLiteral>(); inner_literal_tuple)
|
||||
{
|
||||
if (inner_literal_tuple->value.getType() == Field::Types::Tuple)
|
||||
partition_ast_fields_count = inner_literal_tuple->value.safeGet<Tuple>().size();
|
||||
else
|
||||
partition_ast_fields_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(
|
||||
ErrorCodes::INVALID_PARTITION_VALUE, "Expected tuple for complex partition key, got {}", tuple_ast->name);
|
||||
}
|
||||
}
|
||||
else
|
||||
throw Exception(ErrorCodes::INVALID_PARTITION_VALUE, "Expected tuple for complex partition key, got {}", tuple_ast->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto * arguments_ast = tuple_ast->arguments->as<ASTExpressionList>();
|
||||
if (arguments_ast)
|
||||
partition_ast_fields_count = arguments_ast->children.size();
|
||||
else
|
||||
partition_ast_fields_count = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(
|
||||
ErrorCodes::INVALID_PARTITION_VALUE, "Expected literal or tuple for partition key, got {}", partition_value_ast->getID());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
partition_ast_fields_count = *partition_ast.fields_count;
|
||||
}
|
||||
|
||||
if (format_version < MERGE_TREE_DATA_MIN_FORMAT_VERSION_WITH_CUSTOM_PARTITIONING)
|
||||
@ -5352,9 +5418,8 @@ String MergeTreeData::getPartitionIDFromQuery(const ASTPtr & ast, ContextPtr loc
|
||||
const auto * partition_lit = partition_ast.value->as<ASTLiteral>();
|
||||
if (partition_lit && partition_lit->value.getType() == Field::Types::String)
|
||||
{
|
||||
String partition_id = partition_lit->value.get<String>();
|
||||
MergeTreePartInfo::validatePartitionID(partition_id, format_version);
|
||||
return partition_id;
|
||||
MergeTreePartInfo::validatePartitionID(partition_ast.value->clone(), format_version);
|
||||
return partition_lit->value.get<String>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -5362,35 +5427,48 @@ String MergeTreeData::getPartitionIDFromQuery(const ASTPtr & ast, ContextPtr loc
|
||||
auto metadata_snapshot = getInMemoryMetadataPtr();
|
||||
const Block & key_sample_block = metadata_snapshot->getPartitionKey().sample_block;
|
||||
size_t fields_count = key_sample_block.columns();
|
||||
if (partition_ast.fields_count != fields_count)
|
||||
if (partition_ast_fields_count != fields_count)
|
||||
throw Exception(ErrorCodes::INVALID_PARTITION_VALUE,
|
||||
"Wrong number of fields in the partition expression: {}, must be: {}",
|
||||
partition_ast.fields_count, fields_count);
|
||||
partition_ast_fields_count, fields_count);
|
||||
|
||||
Row partition_row(fields_count);
|
||||
if (fields_count == 0)
|
||||
{
|
||||
/// Function tuple(...) requires at least one argument, so empty key is a special case
|
||||
assert(!partition_ast.fields_count);
|
||||
assert(typeid_cast<ASTFunction *>(partition_ast.value.get()));
|
||||
assert(partition_ast.value->as<ASTFunction>()->name == "tuple");
|
||||
assert(partition_ast.value->as<ASTFunction>()->arguments);
|
||||
auto args = partition_ast.value->as<ASTFunction>()->arguments;
|
||||
assert(!partition_ast_fields_count);
|
||||
assert(typeid_cast<ASTFunction *>(partition_value_ast.get()));
|
||||
assert(partition_value_ast->as<ASTFunction>()->name == "tuple");
|
||||
assert(partition_value_ast->as<ASTFunction>()->arguments);
|
||||
auto args = partition_value_ast->as<ASTFunction>()->arguments;
|
||||
if (!args)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Expected at least one argument in partition AST");
|
||||
bool empty_tuple = partition_ast.value->as<ASTFunction>()->arguments->children.empty();
|
||||
bool empty_tuple = partition_value_ast->as<ASTFunction>()->arguments->children.empty();
|
||||
if (!empty_tuple)
|
||||
throw Exception(ErrorCodes::INVALID_PARTITION_VALUE, "Partition key is empty, expected 'tuple()' as partition key");
|
||||
}
|
||||
else if (fields_count == 1)
|
||||
{
|
||||
ASTPtr partition_value_ast = partition_ast.value;
|
||||
if (auto * tuple = partition_value_ast->as<ASTFunction>())
|
||||
if (auto * tuple = partition_value_ast->as<ASTFunction>(); tuple)
|
||||
{
|
||||
assert(tuple->name == "tuple");
|
||||
assert(tuple->arguments);
|
||||
assert(tuple->arguments->children.size() == 1);
|
||||
partition_value_ast = tuple->arguments->children[0];
|
||||
if (tuple->name == "tuple")
|
||||
{
|
||||
assert(tuple->arguments);
|
||||
assert(tuple->arguments->children.size() == 1);
|
||||
partition_value_ast = tuple->arguments->children[0];
|
||||
}
|
||||
else if (isFunctionCast(tuple))
|
||||
{
|
||||
assert(tuple->arguments);
|
||||
assert(tuple->arguments->children.size() == 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Exception(
|
||||
ErrorCodes::INVALID_PARTITION_VALUE,
|
||||
"Expected literal or tuple for partition key, got {}",
|
||||
partition_value_ast->getID());
|
||||
}
|
||||
}
|
||||
/// Simple partition key, need to evaluate and cast
|
||||
Field partition_key_value = evaluateConstantExpression(partition_value_ast, local_context).first;
|
||||
@ -5399,7 +5477,7 @@ String MergeTreeData::getPartitionIDFromQuery(const ASTPtr & ast, ContextPtr loc
|
||||
else
|
||||
{
|
||||
/// Complex key, need to evaluate, untuple and cast
|
||||
Field partition_key_value = evaluateConstantExpression(partition_ast.value, local_context).first;
|
||||
Field partition_key_value = evaluateConstantExpression(partition_value_ast, local_context).first;
|
||||
if (partition_key_value.getType() != Field::Types::Tuple)
|
||||
throw Exception(ErrorCodes::INVALID_PARTITION_VALUE,
|
||||
"Expected tuple for complex partition key, got {}", partition_key_value.getTypeName());
|
||||
|
@ -723,7 +723,11 @@ public:
|
||||
void checkMutationIsPossible(const MutationCommands & commands, const Settings & settings) const override;
|
||||
|
||||
/// Checks that partition name in all commands is valid
|
||||
void checkAlterPartitionIsPossible(const PartitionCommands & commands, const StorageMetadataPtr & metadata_snapshot, const Settings & settings) const override;
|
||||
void checkAlterPartitionIsPossible(
|
||||
const PartitionCommands & commands,
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
const Settings & settings,
|
||||
ContextPtr local_context) const override;
|
||||
|
||||
/// Change MergeTreeSettings
|
||||
void changeSettings(
|
||||
|
@ -2,7 +2,8 @@
|
||||
#include <IO/ReadBufferFromString.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include "Core/ProtocolDefines.h"
|
||||
#include <Core/ProtocolDefines.h>
|
||||
#include <Parsers/ASTLiteral.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -23,8 +24,15 @@ MergeTreePartInfo MergeTreePartInfo::fromPartName(const String & part_name, Merg
|
||||
throw Exception(ErrorCodes::BAD_DATA_PART_NAME, "Unexpected part name: {} for format version: {}", part_name, format_version);
|
||||
}
|
||||
|
||||
void MergeTreePartInfo::validatePartitionID(const String & partition_id, MergeTreeDataFormatVersion format_version)
|
||||
void MergeTreePartInfo::validatePartitionID(const ASTPtr & partition_id_ast, MergeTreeDataFormatVersion format_version)
|
||||
{
|
||||
std::string partition_id;
|
||||
if (auto * literal = partition_id_ast->as<ASTLiteral>(); literal != nullptr && literal->value.getType() == Field::Types::String)
|
||||
partition_id = literal->value.safeGet<String>();
|
||||
|
||||
else
|
||||
throw Exception(ErrorCodes::INVALID_PARTITION_VALUE, "Partition id must be string literal");
|
||||
|
||||
if (partition_id.empty())
|
||||
throw Exception(ErrorCodes::INVALID_PARTITION_VALUE, "Partition id is empty");
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <base/DayNum.h>
|
||||
#include <IO/ReadBuffer.h>
|
||||
#include <IO/WriteBuffer.h>
|
||||
#include <Parsers/IAST_fwd.h>
|
||||
#include <Storages/MergeTree/MergeTreeDataFormatVersion.h>
|
||||
|
||||
|
||||
@ -119,7 +120,7 @@ struct MergeTreePartInfo
|
||||
void deserialize(ReadBuffer & in);
|
||||
|
||||
/// Simple sanity check for partition ID. Checking that it's not too long or too short, doesn't contain a lot of '_'.
|
||||
static void validatePartitionID(const String & partition_id, MergeTreeDataFormatVersion format_version);
|
||||
static void validatePartitionID(const ASTPtr & partition_id_ast, MergeTreeDataFormatVersion format_version);
|
||||
|
||||
static MergeTreePartInfo fromPartName(const String & part_name, MergeTreeDataFormatVersion format_version);
|
||||
|
||||
|
@ -330,10 +330,11 @@ Pipe StorageMaterializedView::alterPartition(
|
||||
}
|
||||
|
||||
void StorageMaterializedView::checkAlterPartitionIsPossible(
|
||||
const PartitionCommands & commands, const StorageMetadataPtr & metadata_snapshot, const Settings & settings) const
|
||||
const PartitionCommands & commands, const StorageMetadataPtr & metadata_snapshot,
|
||||
const Settings & settings, ContextPtr local_context) const
|
||||
{
|
||||
checkStatementCanBeForwarded();
|
||||
getTargetTable()->checkAlterPartitionIsPossible(commands, metadata_snapshot, settings);
|
||||
getTargetTable()->checkAlterPartitionIsPossible(commands, metadata_snapshot, settings, local_context);
|
||||
}
|
||||
|
||||
void StorageMaterializedView::mutate(const MutationCommands & commands, ContextPtr local_context)
|
||||
|
@ -64,7 +64,7 @@ public:
|
||||
|
||||
Pipe alterPartition(const StorageMetadataPtr & metadata_snapshot, const PartitionCommands & commands, ContextPtr context) override;
|
||||
|
||||
void checkAlterPartitionIsPossible(const PartitionCommands & commands, const StorageMetadataPtr & metadata_snapshot, const Settings & settings) const override;
|
||||
void checkAlterPartitionIsPossible(const PartitionCommands & commands, const StorageMetadataPtr & metadata_snapshot, const Settings & settings, ContextPtr local_context) const override;
|
||||
|
||||
void mutate(const MutationCommands & commands, ContextPtr context) override;
|
||||
|
||||
|
@ -115,9 +115,9 @@ public:
|
||||
return getNested()->alterPartition(metadata_snapshot, commands, context);
|
||||
}
|
||||
|
||||
void checkAlterPartitionIsPossible(const PartitionCommands & commands, const StorageMetadataPtr & metadata_snapshot, const Settings & settings) const override
|
||||
void checkAlterPartitionIsPossible(const PartitionCommands & commands, const StorageMetadataPtr & metadata_snapshot, const Settings & settings, ContextPtr context) const override
|
||||
{
|
||||
getNested()->checkAlterPartitionIsPossible(commands, metadata_snapshot, settings);
|
||||
getNested()->checkAlterPartitionIsPossible(commands, metadata_snapshot, settings, context);
|
||||
}
|
||||
|
||||
bool optimize(
|
||||
|
@ -0,0 +1,9 @@
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
141
tests/queries/0_stateless/02897_alter_partition_parameters.sql
Normal file
141
tests/queries/0_stateless/02897_alter_partition_parameters.sql
Normal file
@ -0,0 +1,141 @@
|
||||
DROP TABLE IF EXISTS test;
|
||||
|
||||
CREATE TABLE test
|
||||
(
|
||||
EventDate Date
|
||||
)
|
||||
ENGINE = MergeTree
|
||||
ORDER BY tuple()
|
||||
PARTITION BY toMonday(EventDate);
|
||||
|
||||
INSERT INTO test VALUES(toDate('2023-10-09'));
|
||||
|
||||
SET param_partition='2023-10-09';
|
||||
|
||||
ALTER TABLE test DROP PARTITION {partition:String};
|
||||
|
||||
SELECT count() FROM test;
|
||||
|
||||
INSERT INTO test VALUES(toDate('2023-10-09'));
|
||||
|
||||
ALTER TABLE test DROP PARTITION tuple(toMonday({partition:Date}));
|
||||
|
||||
SELECT count() FROM test;
|
||||
|
||||
INSERT INTO test VALUES(toDate('2023-10-09'));
|
||||
|
||||
-- for some reason only tuples are allowed as non-string arguments
|
||||
ALTER TABLE test DROP PARTITION toMonday({partition:String}); --{clientError 62}
|
||||
|
||||
set param_partition_id = '20231009';
|
||||
|
||||
ALTER TABLE test DROP PARTITION ID {partition_id:String};
|
||||
|
||||
SELECT count() FROM test;
|
||||
|
||||
INSERT INTO test VALUES(toDate('2023-10-09'));
|
||||
|
||||
ALTER TABLE test DROP PARTITION {partition:Date};
|
||||
SELECT count() FROM test;
|
||||
|
||||
DROP TABLE IF EXISTS test;
|
||||
|
||||
DROP TABLE IF EXISTS test2;
|
||||
|
||||
CREATE TABLE test2
|
||||
(
|
||||
a UInt32,
|
||||
b Int64
|
||||
)
|
||||
ENGINE = MergeTree
|
||||
ORDER BY tuple()
|
||||
PARTITION BY (a * b, b * b);
|
||||
|
||||
|
||||
INSERT INTO test2 VALUES(1, 2);
|
||||
|
||||
SET param_first='2';
|
||||
SET param_second='4';
|
||||
|
||||
ALTER TABLE test2 DROP PARTITION tuple({first:UInt32},{second:Int64});
|
||||
|
||||
SELECT count() FROM test2;
|
||||
|
||||
DROP TABLE IF EXISTS test2;
|
||||
DROP TABLE IF EXISTS test3;
|
||||
|
||||
CREATE TABLE test3
|
||||
(
|
||||
a UInt32,
|
||||
b Int64
|
||||
)
|
||||
ENGINE = MergeTree
|
||||
ORDER BY tuple()
|
||||
PARTITION BY a;
|
||||
|
||||
INSERT INTO test3 VALUES(1, 2);
|
||||
|
||||
SET param_simple='1';
|
||||
|
||||
ALTER TABLE test3 DROP PARTITION {simple:String};
|
||||
|
||||
SELECT count() FROM test3;
|
||||
|
||||
DROP TABLE IF EXISTS test3;
|
||||
|
||||
DROP TABLE IF EXISTS test4;
|
||||
|
||||
CREATE TABLE test4 (EventDate Date) ENGINE = MergeTree() ORDER BY tuple() PARTITION BY EventDate;
|
||||
|
||||
INSERT INTO test4 VALUES(toDate('2023-10-09'));
|
||||
|
||||
SET param_partition='2023-10-09';
|
||||
|
||||
ALTER TABLE test4 ON CLUSTER 'test_shard_localhost' DROP PARTITION {partition:String} FORMAT Null;
|
||||
|
||||
SELECT count() FROM test4;
|
||||
|
||||
DROP TABLE IF EXISTS test4;
|
||||
|
||||
DROP TABLE IF EXISTS test5;
|
||||
|
||||
CREATE TABLE test5
|
||||
(
|
||||
a UInt32,
|
||||
b Int64
|
||||
)
|
||||
ENGINE = MergeTree
|
||||
ORDER BY tuple()
|
||||
PARTITION BY (a, b);
|
||||
|
||||
INSERT INTO test5 VALUES(1, 2);
|
||||
|
||||
SET param_f='1';
|
||||
SET param_s='2';
|
||||
|
||||
ALTER TABLE test5 DROP PARTITION ({f:UInt32}, 2);
|
||||
|
||||
SELECT count() FROM test5;
|
||||
|
||||
DROP TABLE IF EXISTS test5;
|
||||
|
||||
DROP TABLE IF EXISTS test6;
|
||||
|
||||
CREATE TABLE test6
|
||||
(
|
||||
a UInt32,
|
||||
b Int64
|
||||
)
|
||||
ENGINE = MergeTree
|
||||
ORDER BY tuple()
|
||||
PARTITION BY (a, b);
|
||||
|
||||
INSERT INTO test6 VALUES(1, 2);
|
||||
|
||||
SET param_tuple=(1, 2);
|
||||
|
||||
ALTER TABLE test6 DROP PARTITION {tuple:Tuple(UInt32, Int64)};
|
||||
|
||||
SELECT count() FROM test6;
|
||||
|
||||
DROP TABLE IF EXISTS test6;
|
Loading…
Reference in New Issue
Block a user