Repository: ClickHouse/ClickHouse
File: src/Processors/QueryPlan/PartsSplitter.cpp (C++)

Code before:

            selected_ranges.push_back(
                {index_access.getValue(part_index, range.begin), false, range, part_index,
                    PartsRangesIterator::EventType::RangeStart, true});

            const auto & range_end_value = index_access.getValue(part_index, range.end);
            if (selected_upper_bound.value.empty() || (compareValues(range_end_value, selected_upper_bound.value, false) > 0))
                selected_upper_bound = {range_end_value, false, range, part_index, PartsRangesIterator::EventType::RangeStart, true};

            for (auto i = range.begin; i < range.end; ++i)
               part_selected_ranges[part_index].push_back(i);
        }
    }

    if (selected_ranges.empty())
        return result.getCurrentRangesInDataParts();

    ::sort(selected_ranges.begin(), selected_ranges.end());

    const PartsRangesIterator selected_lower_bound = selected_ranges[0];

    /// Find the ranges that were rejected in each part. We use the 'ranges_snapshot_after_pk_analysis'
    /// member because ranges already rejected by pk analysis don't need to be checked again for intersection.
    for (size_t part_index = 0; part_index < ranges_in_data_parts.size(); ++part_index)
    {
        if (!ranges_in_data_parts[part_index].ranges_snapshot_after_pk_analysis)
            continue;

        auto part_ranges_after_pk_analysis_begin = ranges_in_data_parts[part_index].ranges_snapshot_after_pk_analysis->begin()->begin;
        auto part_ranges_after_pk_analysis_end = ranges_in_data_parts[part_index].ranges_snapshot_after_pk_analysis->back().end;
        const auto & part_lower_bound = index_access.getValue(part_index, part_ranges_after_pk_analysis_begin);
        const auto & part_upper_bound = index_access.getValue(part_index, part_ranges_after_pk_analysis_end);

        if ((compareValues(selected_lower_bound.value, part_upper_bound, false) > 0) ||
            (compareValues(selected_upper_bound.value, part_lower_bound, false) < 0))
        {
            continue; /// early exit, intersection infeasible in this part
        }

        /// The selected lower bound could be 'fqrst' and granule PK ranges could be -
        ///      abcde,...,fcedr, ffagj, fqrst, fqrst, fqrst, ghyrw ....
        /// candidates_start needs to point to granule starting at "ffagj"
        auto candidates_start = index_access.findRightmostMarkLessThanValueInRange(part_index, selected_lower_bound.value, MarkRange{part_ranges_after_pk_analysis_begin, part_ranges_after_pk_analysis_end + 1}, false);
        if (!candidates_start)
            candidates_start = 0;

        auto candidates_end = index_access.findLeftmostMarkGreaterThanValueInRange(part_index, selected_upper_bound.value, MarkRange{part_ranges_after_pk_analysis_begin, part_ranges_after_pk_analysis_end + 1}, false);
        if (!candidates_end)
            candidates_end = part_ranges_after_pk_analysis_end;
        if (candidates_end != 0) /// Come back by 1 because we are now 1 past the upper bound or at the final mark
            candidates_end = candidates_end.value() - 1;

Review comment (suggestion):
If I'm not mistaken, `ranges_snapshot_after_pk_analysis` should always be initialised in cases when we can reach this code. If that's true, we should throw a logical error rather than continue.

Code after:

            selected_ranges.push_back(
                {index_access.getValue(part_index, range.begin), false, range, part_index,
                    PartsRangesIterator::EventType::RangeStart, true});

            const auto & range_end_value = index_access.getValue(part_index, range.end);
            if (selected_upper_bound.value.empty() || (compareValues(range_end_value, selected_upper_bound.value, false) > 0))
                selected_upper_bound = {range_end_value, false, range, part_index, PartsRangesIterator::EventType::RangeStart, true};

            for (auto i = range.begin; i < range.end; ++i)
               part_selected_ranges[part_index].push_back(i);
        }
    }

    if (selected_ranges.empty())
        return result.getCurrentRangesInDataParts();

    ::sort(selected_ranges.begin(), selected_ranges.end());

    const PartsRangesIterator selected_lower_bound = selected_ranges[0];

    /// Find the ranges that were rejected in each part. We use the 'ranges_snapshot_after_pk_analysis'
    /// member because ranges already rejected by pk analysis don't need to be checked again for intersection.
    for (size_t part_index = 0; part_index < ranges_in_data_parts.size(); ++part_index)
    {
        if (!ranges_in_data_parts[part_index].ranges_snapshot_after_pk_analysis)
            throw Exception(ErrorCodes::LOGICAL_ERROR, "Expected ranges selected by primary key for part {}",
                    ranges_in_data_parts[part_index].data_part->name);

        const auto & ranges = *ranges_in_data_parts[part_index].ranges_snapshot_after_pk_analysis;
        const auto part_ranges_after_pk_analysis_begin = ranges.front().begin;
        const auto part_ranges_after_pk_analysis_end = ranges.back().end;
        const auto & part_lower_bound = index_access.getValue(part_index, part_ranges_after_pk_analysis_begin);
        const auto & part_upper_bound = index_access.getValue(part_index, part_ranges_after_pk_analysis_end);

        if ((compareValues(selected_lower_bound.value, part_upper_bound, false) > 0) ||
            (compareValues(selected_upper_bound.value, part_lower_bound, false) < 0))
        {
            continue; /// early exit, intersection infeasible in this part
        }

        /// The selected lower bound could be 'fqrst' and granule PK ranges could be -
        ///      abcde,...,fcedr, ffagj, fqrst, fqrst, fqrst, ghyrw ....
        /// candidates_start needs to point to granule starting at "ffagj"
        auto candidates_start = index_access.findRightmostMarkLessThanValueInRange(part_index, selected_lower_bound.value, MarkRange{part_ranges_after_pk_analysis_begin, part_ranges_after_pk_analysis_end + 1}, false);
        if (!candidates_start)
            candidates_start = 0;

        auto candidates_end = index_access.findLeftmostMarkGreaterThanValueInRange(part_index, selected_upper_bound.value, MarkRange{part_ranges_after_pk_analysis_begin, part_ranges_after_pk_analysis_end + 1}, false);
        if (!candidates_end)
            candidates_end = part_ranges_after_pk_analysis_end;