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

Code before:
    result.column_names_to_read = all_column_names;

    /// If there are only virtual columns in the query, you must request at least one non-virtual one.
    if (result.column_names_to_read.empty())
    {
        NamesAndTypesList available_real_columns = metadata_snapshot->getColumns().getAllPhysical();
        result.column_names_to_read.push_back(ExpressionActions::getSmallestColumn(available_real_columns).name);
    }

    // Build and check if primary key is used when necessary
    const auto & primary_key = metadata_snapshot->getPrimaryKey();
    const Names & primary_key_column_names = primary_key.column_names;

    if (!indexes)
        buildIndexes(
            indexes,
            query_info_.filter_actions_dag.get(),
            data,
            parts,
            vector_search_parameters,
            top_k_filter_info,
            context_,
            query_info_,
            metadata_snapshot);

    Names primary_key_and_useful_skip_indexes_column_names = primary_key_column_names;
    for (const auto & skip_index : indexes->skip_indexes.useful_indices)
        primary_key_and_useful_skip_indexes_column_names.append_range(skip_index.index->getColumnsRequiredForIndexCalc());

    indexes->use_skip_indexes_on_data_read = supports_skip_indexes_on_data_read;
    if (indexes->part_values && indexes->part_values->empty())
        return std::make_shared<AnalysisResult>(std::move(result));

    if (indexes->key_condition.alwaysUnknownOrTrue())
    {
        if (settings[Setting::force_primary_key])
        {
            throw Exception(ErrorCodes::INDEX_NOT_USED,
                "Primary key ({}) is not used and setting 'force_primary_key' is set",
                fmt::join(primary_key_column_names, ", "));
        }
    } else
    {
        ProfileEvents::increment(ProfileEvents::SelectQueriesWithPrimaryKeyUsage);
    }

    LOG_DEBUG(log, "Key condition: {}", indexes->key_condition.toString());

    if (indexes->part_offset_condition)
        LOG_DEBUG(log, "Part offset condition: {}", indexes->part_offset_condition->toString());


Review comment (performance):
This PR is fine as is. It will help e.g. with bloom filter skip indexes.

What isn't clear to me if distributed index analysis can work with text indexes (which are also skip indexes) too. These use a special path "direct read", explained [here](https://clickhouse.com/docs/engines/table-engines/mergetree-family/textindexes) and code is in src/Processors/QueryPlan/Optimizations/optimizeDirectReadFromTextIndex.cpp. Can be tested as a follow-up.

Code after:

    result.column_names_to_read = all_column_names;

    /// If there are only virtual columns in the query, you must request at least one non-virtual one.
    if (result.column_names_to_read.empty())
    {
        NamesAndTypesList available_real_columns = metadata_snapshot->getColumns().getAllPhysical();
        result.column_names_to_read.push_back(ExpressionActions::getSmallestColumn(available_real_columns).name);
    }

    // Build and check if primary key is used when necessary
    const auto & primary_key = metadata_snapshot->getPrimaryKey();
    const Names & primary_key_column_names = primary_key.column_names;

    if (!indexes)
        buildIndexes(
            indexes,
            query_info_.filter_actions_dag.get(),
            data,
            parts,
            vector_search_parameters,
            top_k_filter_info,
            context_,
            query_info_,
            metadata_snapshot);

    NameSet indexes_column_names(primary_key_column_names.begin(), primary_key_column_names.end());
    for (const auto & skip_index : indexes->skip_indexes.useful_indices)
    {
        const auto & skip_index_required_columns = skip_index.index->getColumnsRequiredForIndexCalc();
        indexes_column_names.insert(skip_index_required_columns.begin(), skip_index_required_columns.end());
    }

    indexes->use_skip_indexes_on_data_read = supports_skip_indexes_on_data_read;
    if (indexes->part_values && indexes->part_values->empty())
        return std::make_shared<AnalysisResult>(std::move(result));

    if (indexes->key_condition.alwaysUnknownOrTrue())
    {
        if (settings[Setting::force_primary_key])
        {
            throw Exception(ErrorCodes::INDEX_NOT_USED,
                "Primary key ({}) is not used and setting 'force_primary_key' is set",
                fmt::join(primary_key_column_names, ", "));
        }
    } else
    {
        ProfileEvents::increment(ProfileEvents::SelectQueriesWithPrimaryKeyUsage);
    }

    LOG_DEBUG(log, "Key condition: {}", indexes->key_condition.toString());