Skip to content

Commit d84fa09

Browse files
authored
Merge pull request #2285 from Altinity/mkmkme/antalya-25.7/hybrid-restart
Antalya 25.8 hybrid: resolve currentDatabase() in hybrid segment metadata
2 parents 412b575 + 6012d16 commit d84fa09

3 files changed

Lines changed: 60 additions & 5 deletions

File tree

‎src/Storages/StorageDistributed.cpp‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#include <Disks/IVolume.h>
1818

19+
#include <Functions/FunctionFactory.h>
20+
1921
#include <Storages/Distributed/DistributedSettings.h>
2022
#include <Storages/Distributed/DistributedSink.h>
2123
#include <Storages/StorageFactory.h>
@@ -161,7 +163,8 @@ void replaceCurrentDatabaseFunction(ASTPtr & ast, const ContextPtr & context)
161163

162164
if (auto * func = ast->as<ASTFunction>())
163165
{
164-
if (func->name == "currentDatabase")
166+
/// `currentDatabase` is reachable through its aliases, so get a canonical name
167+
if (getFunctionCanonicalNameIfAny(func->name) == "currentDatabase")
165168
{
166169
ast = evaluateConstantExpressionForDatabaseName(ast, context);
167170
return;
@@ -2699,12 +2702,12 @@ void registerStorageHybrid(StorageFactory & factory)
26992702
"Argument #{}: additional table function must be a valid table function, got: {}", i, func->name);
27002703
}
27012704

2702-
// Normalize arguments (evaluate `currentDatabase()`, expand named collections, etc.).
2703-
// TableFunctionFactory::get mutates the AST in-place inside TableFunctionRemote::parseArguments.
2704-
ASTPtr normalized_table_function_ast = table_function_ast->clone();
2705+
// Evaluate `currentDatabase()` on the original argument, since that one is serialized to metadata.
2706+
// The clone is for TableFunctionFactory::get, which mutates the AST in-place inside TableFunctionRemote::parseArguments.
2707+
replaceCurrentDatabaseFunction(engine_args[i], local_context);
2708+
ASTPtr normalized_table_function_ast = engine_args[i]->clone();
27052709
auto additional_table_function = TableFunctionFactory::instance().get(normalized_table_function_ast, local_context);
27062710
ColumnsDescription segment_columns = additional_table_function->getActualTableStructure(local_context, true);
2707-
replaceCurrentDatabaseFunction(normalized_table_function_ast, local_context);
27082711

27092712
validate_segment_schema(segment_columns, normalized_table_function_ast->formatForLogging());
27102713

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
4
2+
4
3+
4
4+
4
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
-- Reproduce the Hybrid currentDatabase() metadata bug without a server restart.
2+
-- Detaching the table and re-attaching it while a different database is current
3+
-- mimics the missing session-database context that startup ATTACH has. Before the
4+
-- fix the stored metadata keeps currentDatabase(), which then resolves to the wrong
5+
-- database and the segment fails to attach with UNKNOWN_TABLE.
6+
7+
SET allow_experimental_hybrid_table = 1;
8+
9+
CREATE TABLE {CLICKHOUSE_DATABASE:Identifier}.local_hot (ts DateTime, value UInt64) ENGINE = MergeTree ORDER BY ts;
10+
CREATE TABLE {CLICKHOUSE_DATABASE:Identifier}.local_cold (ts DateTime, value UInt64) ENGINE = MergeTree ORDER BY ts;
11+
INSERT INTO {CLICKHOUSE_DATABASE:Identifier}.local_hot VALUES ('2025-10-15', 1), ('2025-11-01', 2);
12+
INSERT INTO {CLICKHOUSE_DATABASE:Identifier}.local_cold VALUES ('2025-08-01', 3), ('2025-06-15', 4);
13+
14+
-- Create with the test database as current so currentDatabase() resolves to it.
15+
USE {CLICKHOUSE_DATABASE:Identifier};
16+
CREATE TABLE {CLICKHOUSE_DATABASE:Identifier}.hybrid_t (ts DateTime, value UInt64)
17+
ENGINE = Hybrid(
18+
remote('localhost:9000', {CLICKHOUSE_DATABASE:String}, 'local_hot'), ts > '2025-09-01',
19+
remote('localhost:9000', currentDatabase(), 'local_cold'), ts <= '2025-09-01'
20+
);
21+
22+
SELECT count() FROM {CLICKHOUSE_DATABASE:Identifier}.hybrid_t;
23+
24+
-- Same table through the DATABASE() alias of currentDatabase(): the metadata rewrite
25+
-- has to canonicalize the function name instead of comparing it literally.
26+
CREATE TABLE {CLICKHOUSE_DATABASE:Identifier}.hybrid_alias_t (ts DateTime, value UInt64)
27+
ENGINE = Hybrid(
28+
remote('localhost:9000', {CLICKHOUSE_DATABASE:String}, 'local_hot'), ts > '2025-09-01',
29+
remote('localhost:9000', DATABASE(), 'local_cold'), ts <= '2025-09-01'
30+
);
31+
32+
SELECT count() FROM {CLICKHOUSE_DATABASE:Identifier}.hybrid_alias_t;
33+
34+
-- Detach, switch the current database, then re-attach. currentDatabase() in the
35+
-- stored metadata now resolves against the other database, whose local_cold does
36+
-- not exist. With the fix the metadata holds the resolved name, so this succeeds.
37+
DETACH TABLE {CLICKHOUSE_DATABASE:Identifier}.hybrid_t;
38+
DETACH TABLE {CLICKHOUSE_DATABASE:Identifier}.hybrid_alias_t;
39+
CREATE DATABASE IF NOT EXISTS {CLICKHOUSE_DATABASE_1:Identifier};
40+
USE {CLICKHOUSE_DATABASE_1:Identifier};
41+
ATTACH TABLE {CLICKHOUSE_DATABASE:Identifier}.hybrid_t;
42+
ATTACH TABLE {CLICKHOUSE_DATABASE:Identifier}.hybrid_alias_t;
43+
44+
SELECT count() FROM {CLICKHOUSE_DATABASE:Identifier}.hybrid_t;
45+
SELECT count() FROM {CLICKHOUSE_DATABASE:Identifier}.hybrid_alias_t;
46+
47+
USE {CLICKHOUSE_DATABASE:Identifier};
48+
DROP DATABASE {CLICKHOUSE_DATABASE_1:Identifier};

0 commit comments

Comments
 (0)