mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-19 16:20:50 +00:00
Merge ae7c9f04c1
into b4504f20bf
This commit is contained in:
commit
0bd87261a5
@ -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);
|
||||
|
117
src/Functions/getMaxTableName.cpp
Normal file
117
src/Functions/getMaxTableName.cpp
Normal 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>();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
ARGUMENT_OUT_OF_BOUND
|
15
tests/queries/0_stateless/03167_improvement_table_name_too_long.sh
Executable file
15
tests/queries/0_stateless/03167_improvement_table_name_too_long.sh
Executable 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;"
|
Loading…
Reference in New Issue
Block a user