Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0992b2c
Merge pull request #72392 from ClickHouse/vdimir/sqashing_rata_race
vdimir Nov 26, 2024
293af9d
Merge pull request #2317 from Altinity/backports/24.8/72392
mkmkme Sep 8, 2026
34cf5e9
docker: fix libssl CVEs for alpine
mkmkme Sep 8, 2026
8f0d5be
Merge pull request #2328 from Altinity/mkmkme/24.8/fix-alpine-cve
mkmkme Sep 9, 2026
5c2baa6
Bump to 24.8.14.10548
MyroTk Sep 9, 2026
30b16f3
CI: use the clickhouse/minio-* mirror image
CarlosFelipeOR Sep 25, 2026
14081de
Do not report builds pruned by the CI cache as missing
CarlosFelipeOR Sep 30, 2026
2fd2cec
Merge pull request #2438 from Altinity/ci/minio-mirror-customizations…
mkmkme Sep 30, 2026
0cef231
Merge branch 'customizations/24.8.14' into ci/builds-report-skip-prun…
mkmkme Sep 30, 2026
b0329ca
Merge pull request #123431 from mkmkme/fix/view-analyzer-distributed-…
clickhouse-gh[bot] Oct 2, 2026
3f9853c
Merge pull request #123436 from mkmkme/fix/row-policy-alias-distribut…
clickhouse-gh[bot] Oct 3, 2026
2cca4dd
Merge pull request #2450 from Altinity/ci/builds-report-skip-pruned-2…
strtgbb Oct 6, 2026
9387e38
Merge branch 'customizations/24.8.14' into backports/stable-24.8/123431
mkmkme Oct 8, 2026
b973ff8
Merge branch 'customizations/24.8.14' into backports/stable-24.8/123436
mkmkme Oct 8, 2026
bcee9ff
Update Dockerfile
MyroTk Oct 8, 2026
380911e
Merge pull request #2518 from Altinity/24.8-stable-keeper-cve
MyroTk Oct 9, 2026
77af932
Merge branch 'customizations/24.8.14' into backports/stable-24.8/123431
mkmkme Oct 9, 2026
8cb93db
Merge branch 'customizations/24.8.14' into backports/stable-24.8/123436
mkmkme Oct 9, 2026
ebd86f0
Merge pull request #2478 from Altinity/backports/stable-24.8/123431
mkmkme Oct 9, 2026
821e979
Merge pull request #2479 from Altinity/backports/stable-24.8/123436
mkmkme Oct 9, 2026
e1e2d75
update all apk packages
MyroTk Oct 9, 2026
d2deb6e
Merge pull request #2532 from Altinity/24.8-stable-keeper-cve
mkmkme Oct 10, 2026
bf22ca6
Merge pull request #83096 from Algunenano/convertToSubcolumns
Algunenano Sep 25, 2025
5877fc2
Merge pull request #2591 from Altinity/backports/stable-24.8/83095
mkmkme Oct 17, 2026
97200eb
Merge branch 'releases/24.8.14' into customizations/24.8.14
zvonand Oct 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docker/keeper/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ RUN arch=${TARGETARCH:-amd64} \
&& ln -s "${rarch}-linux-gnu" /lib/linux-gnu


FROM alpine:3.21.7
FROM alpine

ENV LANG=en_US.UTF-8 \
LANGUAGE=en_US:en \
Expand Down Expand Up @@ -79,6 +79,8 @@ RUN mkdir -p "${DEFAULT_DATA_DIR}" "${DEFAULT_LOG_DIR}" "${DEFAULT_CONFIG_DIR}"
&& chown root:clickhouse "${DEFAULT_LOG_DIR}" \
&& chmod ugo+Xrw -R "${DEFAULT_DATA_DIR}" "${DEFAULT_LOG_DIR}" "${DEFAULT_CONFIG_DIR}"

RUN apk update && apk upgrade --no-cache

# /var/lib/clickhouse is necessary due to the current default configuration for Keeper
VOLUME "${DEFAULT_DATA_DIR}" /var/lib/clickhouse
EXPOSE 2181 10181 44444 9181
Expand Down
2 changes: 2 additions & 0 deletions docker/server/Dockerfile.alpine
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ RUN mkdir -p \
&& chown root:clickhouse "${DEFAULT_LOG_DIR}" \
&& chmod ugo+Xrw -R "${DEFAULT_DATA_DIR}" "${DEFAULT_LOG_DIR}" "${DEFAULT_CLIENT_CONFIG_DIR}" "${DEFAULT_SERVER_CONFIG_DIR}"

RUN apk update && apk upgrade --no-cache

VOLUME "${DEFAULT_DATA_DIR}"
EXPOSE 9000 8123 9009

Expand Down
20 changes: 20 additions & 0 deletions src/Columns/ColumnLowCardinality.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,26 @@ class ColumnLowCardinality final : public COWHelper<IColumnHelper<ColumnLowCardi
callback(dictionary.getColumnUniquePtr());
}

void forEachSubcolumnRecursively(RecursiveColumnCallback callback) const override
{
/** It is important to have both const and non-const versions here.
* The behavior of ColumnUnique::forEachSubcolumnRecursively differs between const and non-const versions.
* The non-const version will update a field in ColumnUnique.
* In the meantime, the default implementation IColumn::forEachSubcolumnRecursively uses const_cast,
* so when the const version is called, the field will still be mutated.
* This can lead to a data race if constness is expected.
*/
callback(*idx.getPositionsPtr());
idx.getPositionsPtr()->forEachSubcolumnRecursively(callback);

/// Column doesn't own dictionary if it's shared.
if (!dictionary.isShared())
{
callback(*dictionary.getColumnUniquePtr());
dictionary.getColumnUniquePtr()->forEachSubcolumnRecursively(callback);
}
}

void forEachSubcolumnRecursively(RecursiveMutableColumnCallback callback) override
{
callback(*idx.getPositionsPtr());
Expand Down
33 changes: 21 additions & 12 deletions src/Interpreters/InterpreterSelectQuery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,13 @@ FilterDAGInfoPtr generateFilterActions(
select_ast->setExpression(ASTSelectQuery::Expression::SELECT, std::make_shared<ASTExpressionList>());
auto expr_list = select_ast->select();

/// The first column is our filter expression.
/// the row_policy_filter_expression should be cloned, because it may be changed by TreeRewriter.
/// which make it possible an invalid expression, although it may be valid in whole select.
expr_list->children.push_back(row_policy_filter_expression->clone());
/// The first column is our filter expression. Clone it because `TreeRewriter` can change the AST.
auto filter_expression = row_policy_filter_expression->clone();

/// `TreeRewriter` expands table aliases only on the initiator, but these filters are also
/// created on shards. An `ALIAS` used by a row policy must be evaluated before the read filter.
replaceAliasColumnsInQuery(filter_expression, metadata_snapshot->getColumns(), {}, context);
expr_list->children.push_back(std::move(filter_expression));

/// Keep columns that are required after the filter actions.
for (const auto & column_str : prerequisite_columns)
Expand Down Expand Up @@ -2127,7 +2130,7 @@ void InterpreterSelectQuery::addPrewhereAliasActions()
}

/// Set of all (including ALIAS) required columns for PREWHERE
auto get_prewhere_columns = [&]()
auto get_prewhere_columns = [&](bool include_row_level_filter)
{
NameSet columns;

Expand All @@ -2138,7 +2141,7 @@ void InterpreterSelectQuery::addPrewhereAliasActions()
columns.insert(prewhere_required_columns.begin(), prewhere_required_columns.end());
}

if (row_level_filter)
if (row_level_filter && include_row_level_filter)
{
auto row_level_required_columns = row_level_filter->actions.getRequiredColumns().getNames();
columns.insert(row_level_required_columns.begin(), row_level_required_columns.end());
Expand All @@ -2156,14 +2159,14 @@ void InterpreterSelectQuery::addPrewhereAliasActions()
/// before any other executions.
if (alias_columns_required)
{
NameSet required_columns_from_prewhere = get_prewhere_columns();
/// The row-level filter runs before `PREWHERE`, but its inputs are not produced by
/// `PREWHERE`. Keep them in the alias actions for queries that still need them.
NameSet required_columns_from_prewhere = get_prewhere_columns(/*include_row_level_filter=*/ false);
NameSet required_aliases_from_prewhere; /// Set of ALIAS required columns for PREWHERE

/// Expression, that contains all raw required columns
ASTPtr required_columns_all_expr = std::make_shared<ASTExpressionList>();

/// Expression, that contains raw required columns for PREWHERE
ASTPtr required_columns_from_prewhere_expr = std::make_shared<ASTExpressionList>();

/// Sort out already known required columns between expressions,
/// also populate `required_aliases_from_prewhere`.
Expand All @@ -2189,8 +2192,6 @@ void InterpreterSelectQuery::addPrewhereAliasActions()

if (required_columns_from_prewhere.contains(column))
{
required_columns_from_prewhere_expr->children.emplace_back(std::move(column_expr));

if (is_alias)
required_aliases_from_prewhere.insert(column);
}
Expand Down Expand Up @@ -2258,7 +2259,7 @@ void InterpreterSelectQuery::addPrewhereAliasActions()
const auto & supported_prewhere_columns = storage->supportedPrewhereColumns();
if (supported_prewhere_columns.has_value())
{
NameSet required_columns_from_prewhere = get_prewhere_columns();
NameSet required_columns_from_prewhere = get_prewhere_columns(/*include_row_level_filter=*/ true);

for (const auto & column_name : required_columns_from_prewhere)
{
Expand Down Expand Up @@ -3200,8 +3201,16 @@ void InterpreterSelectQuery::initSettings()
{
auto & query = getSelectQuery();
if (query.settings())
{
InterpreterSetQuery(query.settings(), context).executeForCurrentContext(options.ignore_setting_constraints);

/// The old interpreter disabled the analyzer in `IInterpreterUnionOrSelectQuery`, but a `SELECT`
/// stored in a `VIEW` may enable it again here. Storages must not use analyzer-only query tree
/// paths (for example, `StorageDistributed::read`) when this interpreter built the query.
if (context->getSettingsRef().allow_experimental_analyzer)
context->setSetting("allow_experimental_analyzer", false);
}

const auto & client_info = context->getClientInfo();
auto min_major = DBMS_MIN_MAJOR_VERSION_WITH_CURRENT_AGGREGATION_VARIANT_SELECTION_METHOD;
auto min_minor = DBMS_MIN_MINOR_VERSION_WITH_CURRENT_AGGREGATION_VARIANT_SELECTION_METHOD;
Expand Down
26 changes: 13 additions & 13 deletions src/Processors/Executors/ExecutingGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ bool ExecutingGraph::addEdges(uint64_t node)
return was_edge_added;
}

bool ExecutingGraph::expandPipeline(std::stack<uint64_t> & stack, uint64_t pid)
bool ExecutingGraph::expandPipeline(boost::container::devector<uint64_t> & stack, uint64_t pid)
{
auto & cur_node = *nodes[pid];
Processors new_processors;
Expand Down Expand Up @@ -174,7 +174,7 @@ bool ExecutingGraph::expandPipeline(std::stack<uint64_t> & stack, uint64_t pid)
if (node.status == ExecutingGraph::ExecStatus::Idle)
{
node.status = ExecutingGraph::ExecStatus::Preparing;
stack.push(updated_node);
stack.push_front(updated_node);
}
}

Expand Down Expand Up @@ -215,9 +215,9 @@ void ExecutingGraph::initializeExecution(Queue & queue)

bool ExecutingGraph::updateNode(uint64_t pid, Queue & queue, Queue & async_queue)
{
std::stack<Edge *> updated_edges;
std::stack<uint64_t> updated_processors;
updated_processors.push(pid);
boost::container::devector<Edge *> updated_edges;
boost::container::devector<uint64_t> updated_processors;
updated_processors.push_back(pid);

std::shared_lock read_lock(nodes_mutex);

Expand All @@ -227,8 +227,8 @@ bool ExecutingGraph::updateNode(uint64_t pid, Queue & queue, Queue & async_queue

if (updated_processors.empty())
{
auto * edge = updated_edges.top();
updated_edges.pop();
auto * edge = updated_edges.front();
updated_edges.pop_front();

/// Here we have ownership on edge, but node can be concurrently accessed.

Expand All @@ -248,7 +248,7 @@ bool ExecutingGraph::updateNode(uint64_t pid, Queue & queue, Queue & async_queue
if (status == ExecutingGraph::ExecStatus::Idle)
{
node.status = ExecutingGraph::ExecStatus::Preparing;
updated_processors.push(edge->to);
updated_processors.push_front(edge->to);
stack_top_lock = std::move(lock);
}
else
Expand All @@ -258,8 +258,8 @@ bool ExecutingGraph::updateNode(uint64_t pid, Queue & queue, Queue & async_queue

if (!updated_processors.empty())
{
pid = updated_processors.top();
updated_processors.pop();
pid = updated_processors.front();
updated_processors.pop_front();

/// In this method we have ownership on node.
auto & node = *nodes[pid];
Expand Down Expand Up @@ -364,14 +364,14 @@ bool ExecutingGraph::updateNode(uint64_t pid, Queue & queue, Queue & async_queue
for (auto it = node.post_updated_output_ports.rbegin(); it != node.post_updated_output_ports.rend(); ++it)
{
auto * edge = static_cast<ExecutingGraph::Edge *>(*it);
updated_edges.push(edge);
updated_edges.push_front(edge);
edge->update_info.trigger();
}

for (auto it = node.post_updated_input_ports.rbegin(); it != node.post_updated_input_ports.rend(); ++it)
{
auto * edge = static_cast<ExecutingGraph::Edge *>(*it);
updated_edges.push(edge);
updated_edges.push_front(edge);
edge->update_info.trigger();
}

Expand All @@ -392,7 +392,7 @@ bool ExecutingGraph::updateNode(uint64_t pid, Queue & queue, Queue & async_queue
read_lock.lock();

/// Add itself back to be prepared again.
updated_processors.push(pid);
updated_processors.push_front(pid);
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/Processors/Executors/ExecutingGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
#include <Common/AllocatorWithMemoryTracking.h>
#include <mutex>
#include <queue>
#include <stack>
#include <vector>

#include <boost/container/devector.hpp>

namespace DB
{
Expand Down Expand Up @@ -120,7 +120,7 @@ class ExecutingGraph

/// This queue can grow a lot and lead to OOM. That is why we use non-default
/// allocator for container which throws exceptions in operator new
using DequeWithMemoryTracker = std::deque<ExecutingGraph::Node *, AllocatorWithMemoryTracking<ExecutingGraph::Node *>>;
using DequeWithMemoryTracker = boost::container::devector<ExecutingGraph::Node *, AllocatorWithMemoryTracking<ExecutingGraph::Node *>>;
using Queue = std::queue<ExecutingGraph::Node *, DequeWithMemoryTracker>;

using NodePtr = std::unique_ptr<Node>;
Expand Down Expand Up @@ -155,7 +155,7 @@ class ExecutingGraph

/// Update graph after processor (pid) returned ExpandPipeline status.
/// All new nodes and nodes with updated ports are pushed into stack.
bool expandPipeline(std::stack<uint64_t> & stack, uint64_t pid);
bool expandPipeline(boost::container::devector<uint64_t> & stack, uint64_t pid);

std::shared_ptr<Processors> processors;
std::vector<bool> source_processors;
Expand Down
2 changes: 1 addition & 1 deletion src/Processors/Executors/ExecutorTasks.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class ExecutorTasks
using Stack = std::stack<UInt64>;
/// This queue can grow a lot and lead to OOM. That is why we use non-default
/// allocator for container which throws exceptions in operator new
using DequeWithMemoryTracker = std::deque<ExecutingGraph::Node *, AllocatorWithMemoryTracking<ExecutingGraph::Node *>>;
using DequeWithMemoryTracker = boost::container::devector<ExecutingGraph::Node *, AllocatorWithMemoryTracking<ExecutingGraph::Node *>>;
using Queue = std::queue<ExecutingGraph::Node *, DequeWithMemoryTracker>;

void finish();
Expand Down
5 changes: 3 additions & 2 deletions src/Processors/Executors/PipelineExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
#include <Common/ConcurrencyControl.h>
#include <Common/AllocatorWithMemoryTracking.h>

#include <deque>
#include <queue>
#include <memory>

#include <boost/container/devector.hpp>


namespace DB
{
Expand Down Expand Up @@ -93,7 +94,7 @@ class PipelineExecutor

/// This queue can grow a lot and lead to OOM. That is why we use non-default
/// allocator for container which throws exceptions in operator new
using DequeWithMemoryTracker = std::deque<ExecutingGraph::Node *, AllocatorWithMemoryTracking<ExecutingGraph::Node *>>;
using DequeWithMemoryTracker = boost::container::devector<ExecutingGraph::Node *, AllocatorWithMemoryTracking<ExecutingGraph::Node *>>;
using Queue = std::queue<ExecutingGraph::Node *, DequeWithMemoryTracker>;

void initializeExecution(size_t num_threads, bool concurrency_control); /// Initialize executor contexts and task_queue.
Expand Down
Loading
Loading