Repository: ClickHouse/ClickHouse
File: src/QueryPipeline/BlockIO.cpp (C++)

Code before:
    releaseQuerySlot();
    pipeline.reset();
    process_list_entries.clear();

    /// TODO Do we need also reset callbacks? In which order?
}

BlockIO & BlockIO::operator= (BlockIO && rhs) noexcept
{
    if (this == &rhs)
        return *this;

    /// Explicitly reset fields, so everything is destructed in right order
    reset();

    process_list_entries    = std::move(rhs.process_list_entries);
    query_metadata_cache    = std::move(rhs.query_metadata_cache);
    pipeline                = std::move(rhs.pipeline);

    finalize_query_pipeline = std::move(rhs.finalize_query_pipeline);
    finish_callbacks        = std::move(rhs.finish_callbacks);
    exception_callbacks     = std::move(rhs.exception_callbacks);

    null_format             = rhs.null_format;

    return *this;
}

BlockIO::~BlockIO()
{
    reset();
}

void BlockIO::onFinish(std::chrono::system_clock::time_point finish_time)
{
    releaseQuerySlot();
    if (finalize_query_pipeline)
    {
        const QueryPipelineFinalizedInfo query_pipeline_finalized_info = finalize_query_pipeline(std::move(pipeline));
        for (const auto & callback : finish_callbacks)
        {
            callback(query_pipeline_finalized_info, finish_time);
        }
    }
    else
    {
        pipeline.reset();
    }
}

void BlockIO::onException(bool log_as_error)

Review comment (performance):
I think we also need:
- https://github.com/ClickHouse/ClickHouse/pull/95396/changes/d02700909db12eb854516b1b02194179edb4e0af
- and **always** reset `query_metadata_cache` before `pipeline` (some sort of https://github.com/ClickHouse/ClickHouse/pull/95396/changes/1002f7ce907597dbdd030c3a4fec0c7f2cb08eea, but only `BlockIO` part)

Code after:
      *  since in, out and pipeline contain pointer to objects inside process_list_entry (query-level MemoryTracker for example),
      *  which could be used before destroying of in and out.
      *
      *  However, QueryStatus inside process_list_entry holds shared pointers to streams for some reason.
      *  Streams must be destroyed before storage locks, storages and contexts inside pipeline,
      *  so releaseQueryStreams() is required.
      */
    /// TODO simplify it all

    releaseQuerySlot();
    resetPipeline(/*cancel=*/false);
    process_list_entries.clear();

    /// TODO Do we need also reset callbacks? In which order?
}

BlockIO & BlockIO::operator= (BlockIO && rhs) noexcept
{
    if (this == &rhs)
        return *this;

    /// Explicitly reset fields, so everything is destructed in right order
    reset();

    process_list_entries    = std::move(rhs.process_list_entries);
    query_metadata_cache    = std::move(rhs.query_metadata_cache);
    pipeline                = std::move(rhs.pipeline);

    finalize_query_pipeline = std::move(rhs.finalize_query_pipeline);
    finish_callbacks        = std::move(rhs.finish_callbacks);
    exception_callbacks     = std::move(rhs.exception_callbacks);

    null_format             = rhs.null_format;

    return *this;
}

BlockIO::~BlockIO()
{
    reset();
}

void BlockIO::onFinish(std::chrono::system_clock::time_point finish_time)
{
    releaseQuerySlot();
    if (finalize_query_pipeline)
    {
        const QueryPipelineFinalizedInfo query_pipeline_finalized_info = finalize_query_pipeline(std::move(pipeline));
        for (const auto & callback : finish_callbacks)
        {
            callback(query_pipeline_finalized_info, finish_time);