Skip to content

Commit 832b636

Browse files
authored
Merge pull request ClickHouse#89591 from ClickHouse/backport/25.8/89527
Backport ClickHouse#89527 to 25.8: Fix possible "Context has expired" with analyzer and PK IN (subquery) (v2)
2 parents cca7f5c + a05f618 commit 832b636

File tree

4 files changed

+96
-2
lines changed

4 files changed

+96
-2
lines changed

src/Planner/Planner.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,8 +1256,14 @@ void addBuildSubqueriesForSetsStepIfNeeded(
12561256
std::make_shared<GlobalPlannerContext>(nullptr, nullptr, FiltersForTableExpressionMap{}));
12571257
subquery_planner.buildQueryPlanIfNeeded();
12581258

1259-
query_plan.addInterpreterContext(subquery_planner.getPlannerContext()->getQueryContext());
1260-
subquery->setQueryPlan(std::make_unique<QueryPlan>(std::move(subquery_planner).extractQueryPlan()));
1259+
auto subquery_plan = std::move(subquery_planner).extractQueryPlan();
1260+
/// Contexts should be copied into the root query plan, because some functions may
1261+
/// be created using them while this subquery plan will be destroyed after
1262+
/// FutureSetFromSubquery::buildSetInplace(). Otherwise, function execution may fail
1263+
/// with a "Context has expired" exception.
1264+
for (const auto & context : subquery_plan.getInterpretersContexts())
1265+
query_plan.addInterpreterContext(context);
1266+
subquery->setQueryPlan(std::make_unique<QueryPlan>(std::move(subquery_plan)));
12611267
}
12621268

12631269
if (!subqueries.empty())

src/Processors/QueryPlan/QueryPlan.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class QueryPlan
109109
/// Do not allow to change the table while the pipeline alive.
110110
void addTableLock(TableLockHolder lock) { resources.table_locks.emplace_back(std::move(lock)); }
111111
void addInterpreterContext(std::shared_ptr<const Context> context) { resources.interpreter_context.emplace_back(std::move(context)); }
112+
auto getInterpretersContexts() const { return resources.interpreter_context; }
112113
void addStorageHolder(StoragePtr storage) { resources.storage_holders.emplace_back(std::move(storage)); }
113114

114115
void addResources(QueryPlanResourceHolder resources_) { resources = std::move(resources_); }
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
1
2+
1
3+
Testing w/o relying on enable_global_with_statement...
4+
1
5+
1
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
-- Issue: https://github.com/ClickHouse/ClickHouse/issues/89433
2+
3+
DROP TABLE IF EXISTS tbl;
4+
DROP TABLE IF EXISTS join_engine;
5+
6+
CREATE TABLE tbl
7+
(
8+
`id1` LowCardinality(String),
9+
`id2` LowCardinality(String),
10+
`v` Int64
11+
)
12+
ENGINE = MergeTree
13+
ORDER BY (id1, id2, v);
14+
INSERT INTO tbl VALUES ('a', 'b', 1);
15+
CREATE TABLE join_engine
16+
(
17+
`id1` LowCardinality(String),
18+
`id2` LowCardinality(String),
19+
`v` Int64
20+
)
21+
ENGINE = Join(ANY, LEFT, id1, id2);
22+
INSERT INTO join_engine VALUES ('a', 'b', 1);
23+
24+
WITH cte AS
25+
(
26+
SELECT id2
27+
FROM tbl
28+
WHERE joinGet(currentDatabase() || '.join_engine', 'v', id1, id2) = tbl.v
29+
)
30+
SELECT uniq(id2) AS count
31+
FROM
32+
(
33+
-- NOTE: the bug is reproduced only because due to
34+
-- enable_global_with_statement adds "cte" here, but likely it will be
35+
-- fixed one day... so I've added another test below that does not rely
36+
-- on this fact
37+
SELECT *
38+
FROM tbl AS e
39+
WHERE joinGet(currentDatabase() || '.join_engine', 'v', id1, id2) = e.v
40+
)
41+
WHERE id2 IN (
42+
SELECT id2
43+
FROM cte
44+
)
45+
UNION ALL
46+
SELECT uniq(id2) AS count
47+
FROM cte;
48+
49+
SELECT 'Testing w/o relying on enable_global_with_statement...';
50+
--
51+
-- The same as before, but without relying on enable_global_with_statement
52+
--
53+
SELECT uniq(id2) AS count
54+
FROM
55+
(
56+
WITH cte AS
57+
(
58+
SELECT id2
59+
FROM tbl
60+
WHERE joinGet(currentDatabase() || '.join_engine', 'v', id1, id2) = tbl.v
61+
)
62+
SELECT *
63+
FROM tbl AS e
64+
WHERE joinGet(currentDatabase() || '.join_engine', 'v', id1, id2) = e.v
65+
)
66+
WHERE id2 IN (
67+
SELECT id2
68+
FROM
69+
(
70+
SELECT id2
71+
FROM tbl
72+
WHERE joinGet(currentDatabase() || '.join_engine', 'v', id1, id2) = tbl.v
73+
)
74+
)
75+
UNION ALL
76+
SELECT uniq(id2) AS count
77+
FROM
78+
(
79+
SELECT id2
80+
FROM tbl
81+
WHERE joinGet(currentDatabase() || '.join_engine', 'v', id1, id2) = tbl.v
82+
);

0 commit comments

Comments
 (0)