This commit is contained in:
凌涛 2024-09-19 16:00:47 +02:00 committed by GitHub
commit 0bd87261a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 144 additions and 0 deletions

View File

@ -11,6 +11,7 @@
#include <IO/WriteBufferFromFile.h>
#include <IO/WriteHelpers.h>
#include <Interpreters/ApplyWithSubqueryVisitor.h>
#include <Interpreters/executeQuery.h>
#include <Interpreters/Context.h>
#include <Interpreters/DatabaseCatalog.h>
#include <Interpreters/InterpreterCreateQuery.h>
@ -67,6 +68,7 @@ namespace ErrorCodes
extern const int EMPTY_LIST_OF_COLUMNS_PASSED;
extern const int DATABASE_NOT_EMPTY;
extern const int INCORRECT_QUERY;
extern const int ARGUMENT_OUT_OF_BOUND;
}
@ -388,8 +390,17 @@ void DatabaseOnDisk::checkMetadataFilenameAvailability(const String & to_table_n
void DatabaseOnDisk::checkMetadataFilenameAvailabilityUnlocked(const String & to_table_name) const
{
String query = fmt::format("SELECT getMaxTableName('{}')", database_name);
auto mutable_context = std::const_pointer_cast<Context>(getContext());
const auto & res = executeQuery(query, mutable_context, QueryFlags{ .internal = true }).second;
const auto & res_col = res.pipeline.getHeader().getColumnsWithTypeAndName()[0].column;
const auto allowed_max_length = res_col->getUInt(1);
String table_metadata_path = getObjectMetadataPath(to_table_name);
if (escapeForFileName(to_table_name).length() > allowed_max_length)
throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "The max length of table name for database {} is {}, current length is {}",
database_name, allowed_max_length, to_table_name.length());
if (fs::exists(table_metadata_path))
{
fs::path detached_permanently_flag(table_metadata_path + detached_suffix);

View File

@ -0,0 +1,117 @@
#include <Functions/IFunction.h>
#include <Functions/FunctionFactory.h>
#include <Interpreters/Context.h>
#include <Interpreters/DatabaseCatalog.h>
#include <DataTypes/DataTypesNumber.h>
#include <Columns/ColumnString.h>
#include <Columns/ColumnFixedString.h>
#include <Core/Field.h>
#include <filesystem>
#include <Common/escapeForFileName.h>
namespace DB
{
namespace ErrorCodes
{
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int ILLEGAL_COLUMN;
extern const int UNKNOWN_DATABASE;
}
class FunctionGetMaxTableName: public IFunction, WithContext
{
const String db_name;
public:
static constexpr auto name = "getMaxTableNameForDatabase";
static FunctionPtr create(ContextPtr context_)
{
return std::make_shared<FunctionGetMaxTableName>(context_);
}
explicit FunctionGetMaxTableName(ContextPtr context_):WithContext(context_)
{
}
String getName() const override
{
return name;
}
size_t getNumberOfArguments() const override
{
return 1;
}
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{
if (arguments.size() != 1)
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Number of arguments for function {} can't be {}, should be 1", getName(), arguments.size());
WhichDataType which(arguments[0]);
if (!which.isStringOrFixedString())
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}, expected String or FixedString",
arguments[0]->getName(), getName());
return std::make_shared<DataTypeUInt64>();
}
bool isDeterministic() const override { return false; }
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
{
String suffix = ".sql.detached";
String create_directory_name = (std::filesystem::path(getContext()->getPath()) / "metadata").string();
auto max_create_length = pathconf(create_directory_name.data(), _PC_NAME_MAX);
String dropped_directory_name = (std::filesystem::path(getContext()->getPath()) / "metadata_dropped").string();
auto max_dropped_length = pathconf(dropped_directory_name.data(), _PC_NAME_MAX);
size_t allowed_max_length;
if (max_dropped_length == -1)
max_dropped_length = NAME_MAX;
if (max_create_length == -1)
max_create_length = NAME_MAX;
//File name to drop is escaped_db_name.escaped_table_name.uuid.sql
//File name to create is table_name.sql
auto max_to_create = static_cast<size_t>(max_create_length) - suffix.length();
const IColumn * col;
if (!isColumnConst(*arguments[0].column.get()))
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "The argument of function {} must be constant.", getName());
String database_name;
WhichDataType which(arguments[0].type);
if (which.isString())
col = checkAndGetColumn<ColumnString>(arguments[0].column.get());
else
col = checkAndGetColumn<ColumnFixedString>(arguments[0].column.get());
database_name = col->getDataAt(1).toString();
if (!DatabaseCatalog::instance().isDatabaseExist(database_name))
throw Exception(ErrorCodes::UNKNOWN_DATABASE, "Database {} doesn't exist.", database_name);
//36 is prepared for renaming table operation while dropping
//max_to_drop = max_dropped_length - length_of(database_name)- length_of(uuid) - lenght_of('sql' + 3 dots)
auto max_to_drop = static_cast<size_t>(max_dropped_length) - escapeForFileName(database_name).length() - 48;
allowed_max_length = max_to_create > max_to_drop ? max_to_drop : max_to_create;
return DataTypeUInt64().createColumnConst(input_rows_count, allowed_max_length);
}
};
REGISTER_FUNCTION(getMaxTableName)
{
factory.registerFunction<FunctionGetMaxTableName>();
}
}

View File

@ -0,0 +1 @@
ARGUMENT_OUT_OF_BOUND

View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
. "$CUR_DIR"/../shell_config.sh
allowed_name_length=$($CLICKHOUSE_CLIENT -mn --query="SELECT getMaxTableNameForDatabase('$CLICKHOUSE_DATABASE')")
let excess_length=allowed_name_length+1
long_table_name=$(openssl rand -base64 $excess_length | tr -dc A-Za-z | head -c $excess_length)
allowed_table_name=$(openssl rand -base64 $allowed_name_length | tr -dc A-Za-z | head -c $allowed_name_length)
$CLICKHOUSE_CLIENT -mn --query="CREATE TABLE $long_table_name (id UInt32, long_table_name String) Engine=MergeTree() order by id;" 2>&1 | grep -o -m 1 ARGUMENT_OUT_OF_BOUND
$CLICKHOUSE_CLIENT -mn --query="CREATE TABLE $allowed_table_name (id UInt32, allowed_table_name String) Engine=MergeTree() order by id;"
$CLICKHOUSE_CLIENT -mn --query="DROP TABLE $allowed_table_name;"