do not build sets for indexes if they are not used

This commit is contained in:
Anton Popov 2023-03-31 16:06:20 +00:00
parent 5d64841276
commit cdf9cf2fd4
3 changed files with 52 additions and 35 deletions

View File

@ -6067,51 +6067,48 @@ bool MergeTreeData::isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(
} }
bool MergeTreeData::mayBenefitFromIndexForIn( bool MergeTreeData::mayBenefitFromIndexForIn(
const ASTPtr & left_in_operand, ContextPtr, const StorageMetadataPtr & metadata_snapshot) const const ASTPtr & left_in_operand, ContextPtr query_context, const StorageMetadataPtr & metadata_snapshot) const
{ {
/// Make sure that the left side of the IN operator contain part of the key. /// Make sure that the left side of the IN operator contain part of the key.
/// If there is a tuple on the left side of the IN operator, at least one item of the tuple /// If there is a tuple on the left side of the IN operator, at least one item of the tuple
/// must be part of the key (probably wrapped by a chain of some acceptable functions). /// must be part of the key (probably wrapped by a chain of some acceptable functions).
const auto * left_in_operand_tuple = left_in_operand->as<ASTFunction>(); const auto * left_in_operand_tuple = left_in_operand->as<ASTFunction>();
const auto & index_wrapper_factory = MergeTreeIndexFactory::instance(); const auto & index_factory = MergeTreeIndexFactory::instance();
const auto & query_settings = query_context->getSettingsRef();
auto check_for_one_argument = [&](const auto & ast)
{
if (isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(ast, metadata_snapshot))
return true;
if (query_settings.use_skip_indexes)
{
for (const auto & index : metadata_snapshot->getSecondaryIndices())
if (index_factory.get(index)->mayBenefitFromIndexForIn(ast))
return true;
}
if (query_settings.allow_experimental_projection_optimization)
{
for (const auto & projection : metadata_snapshot->getProjections())
if (projection.isPrimaryKeyColumnPossiblyWrappedInFunctions(ast))
return true;
}
return false;
};
if (left_in_operand_tuple && left_in_operand_tuple->name == "tuple") if (left_in_operand_tuple && left_in_operand_tuple->name == "tuple")
{ {
for (const auto & item : left_in_operand_tuple->arguments->children) for (const auto & item : left_in_operand_tuple->arguments->children)
{ if (check_for_one_argument(item))
if (isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(item, metadata_snapshot))
return true;
for (const auto & index : metadata_snapshot->getSecondaryIndices())
if (index_wrapper_factory.get(index)->mayBenefitFromIndexForIn(item))
return true;
for (const auto & projection : metadata_snapshot->getProjections())
{
if (projection.isPrimaryKeyColumnPossiblyWrappedInFunctions(item))
return true;
}
}
/// The tuple itself may be part of the primary key, so check that as a last resort.
if (isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(left_in_operand, metadata_snapshot))
return true;
for (const auto & projection : metadata_snapshot->getProjections())
{
if (projection.isPrimaryKeyColumnPossiblyWrappedInFunctions(left_in_operand))
return true;
}
return false;
}
else
{
for (const auto & index : metadata_snapshot->getSecondaryIndices())
if (index_wrapper_factory.get(index)->mayBenefitFromIndexForIn(left_in_operand))
return true; return true;
for (const auto & projection : metadata_snapshot->getProjections()) /// The tuple itself may be part of the primary key
{ /// or skip index, so check that as a last resort.
if (projection.isPrimaryKeyColumnPossiblyWrappedInFunctions(left_in_operand))
return true;
}
return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(left_in_operand, metadata_snapshot);
} }
return check_for_one_argument(left_in_operand);
} }
using PartitionIdToMaxBlock = std::unordered_map<String, Int64>; using PartitionIdToMaxBlock = std::unordered_map<String, Int64>;

View File

@ -0,0 +1,20 @@
DROP TABLE IF EXISTS t_skip_index_in;
CREATE TABLE t_skip_index_in
(
a String,
b String,
c String,
INDEX idx_c c TYPE bloom_filter GRANULARITY 1
)
ENGINE = MergeTree
ORDER BY (a, b);
INSERT INTO t_skip_index_in VALUES ('a', 'b', 'c');
-- This query checks that set is not being built if indexes are not used,
-- because with EXPLAIN the set will be built only for analysis of indexes.
EXPLAIN SELECT count() FROM t_skip_index_in WHERE c IN (SELECT throwIf(1)) SETTINGS use_skip_indexes = 0 FORMAT Null;
EXPLAIN SELECT count() FROM t_skip_index_in WHERE c IN (SELECT throwIf(1)) SETTINGS use_skip_indexes = 1; -- { serverError FUNCTION_THROW_IF_VALUE_IS_NON_ZERO }
DROP TABLE t_skip_index_in;