ClickHouse startup error when loading a distributed table that depends on a dictionary (#48419)

* Test for start failure.

* Handling the dictionary as shard key.

* Added integration test and fixed style

* Revert extra test

* Fix style

* Fix style

* Refactoring

* Fix build

* style fix
This commit is contained in:
MikhailBurdukov 2023-04-12 15:06:02 +03:00 committed by GitHub
parent f66e9c5a26
commit 2cd3512a5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 5 deletions

View File

@ -115,10 +115,13 @@ void DDLLoadingDependencyVisitor::visit(const ASTStorage & storage, Data & data)
{
if (!storage.engine)
return;
if (storage.engine->name != "Dictionary")
return;
extractTableNameFromArgument(*storage.engine, data, 0);
if (storage.engine->name == "Distributed")
/// Checks that dict* expression was used as sharding_key and builds dependency between the dictionary and current table.
/// Distributed(logs, default, hits[, sharding_key[, policy_name]])
extractTableNameFromArgument(*storage.engine, data, 3);
else if (storage.engine->name == "Dictionary")
extractTableNameFromArgument(*storage.engine, data, 0);
}
@ -131,7 +134,29 @@ void DDLLoadingDependencyVisitor::extractTableNameFromArgument(const ASTFunction
QualifiedTableName qualified_name;
const auto * arg = function.arguments->as<ASTExpressionList>()->children[arg_idx].get();
if (const auto * literal = arg->as<ASTLiteral>())
if (const auto * dict_function = arg->as<ASTFunction>())
{
if (!functionIsDictGet(dict_function->name))
return;
/// Get the dictionary name from `dict*` function.
const auto * literal_arg = dict_function->arguments->as<ASTExpressionList>()->children[0].get();
const auto * dictionary_name = literal_arg->as<ASTLiteral>();
if (!dictionary_name)
return;
if (dictionary_name->value.getType() != Field::Types::String)
return;
auto maybe_qualified_name = QualifiedTableName::tryParseFromString(dictionary_name->value.get<String>());
if (!maybe_qualified_name)
return;
qualified_name = std::move(*maybe_qualified_name);
}
else if (const auto * literal = arg->as<ASTLiteral>())
{
if (literal->value.getType() != Field::Types::String)
return;
@ -167,5 +192,4 @@ void DDLLoadingDependencyVisitor::extractTableNameFromArgument(const ASTFunction
}
data.dependencies.emplace(std::move(qualified_name));
}
}

View File

@ -154,3 +154,40 @@ def test_dependency_via_dictionary_database(node):
node.query(f"DROP DICTIONARY IF EXISTS {d_name} SYNC")
node.query("DROP DATABASE dict_db SYNC")
node.restart_clickhouse()
@pytest.mark.parametrize("node", nodes)
def test_dependent_dict_table_distr(node):
query = node.query
query("CREATE DATABASE test_db;")
query(
"CREATE TABLE test_db.test(id UInt32,data UInt32,key1 UInt8,key2 UInt8) ENGINE=MergeTree ORDER BY id;"
)
query(
"INSERT INTO test_db.test SELECT abs(rand32())%100, rand32()%1000, abs(rand32())%1, abs(rand32())%1 FROM numbers(100);"
)
query(
"CREATE TABLE test_db.dictback (key1 UInt8,key2 UInt8, value UInt8) ENGINE=MergeTree ORDER BY key1;"
)
query("INSERT INTO test_db.dictback VALUES (0,0,0);")
query(
"CREATE DICTIONARY test_db.mdict (key1 UInt8,key2 UInt8, value UInt8) PRIMARY KEY key1,key2"
" SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() DB 'test_db' TABLE 'dictback'))"
" LIFETIME(MIN 100 MAX 100) LAYOUT(COMPLEX_KEY_CACHE(SIZE_IN_CELLS 1000));"
)
query(
"CREATE TABLE test_db.distr (id UInt32, data UInt32, key1 UInt8, key2 UInt8)"
" ENGINE = Distributed('test_shard_localhost', test_db, test, dictGetOrDefault('test_db.mdict','value',(key1,key2),0));"
)
# Tables should load in the correct order.
node.restart_clickhouse()
query("DETACH TABLE test_db.distr;")
query("ATTACH TABLE test_db.distr;")
node.restart_clickhouse()
query("DROP DATABASE IF EXISTS test_db;")