#include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int LOGICAL_ERROR; } MergeTreeIndexhypothesisMergedCondition::MergeTreeIndexhypothesisMergedCondition( const SelectQueryInfo & query, const ConstraintsDescription & constraints, size_t granularity_) : IMergeTreeIndexMergedCondition(granularity_) { const auto & select = query.query->as(); if (select.where() && select.prewhere()) expression_ast = makeASTFunction( "and", select.where()->clone(), select.prewhere()->clone()); else if (select.where()) expression_ast = select.where()->clone(); else if (select.prewhere()) expression_ast = select.prewhere()->clone(); expression_cnf = std::make_unique( expression_ast ? TreeCNFConverter::toCNF(expression_ast) : CNFQuery::AndGroup{}); addConstraints(constraints); } void MergeTreeIndexhypothesisMergedCondition::addIndex(const MergeTreeIndexPtr & index) { if (!index->isMergeable() || index->getGranularity() != granularity) throw Exception(ErrorCodes::LOGICAL_ERROR, "Index {} can not be merged", index->index.type); const auto hypothesis_index = std::dynamic_pointer_cast(index); if (!hypothesis_index) throw Exception(ErrorCodes::LOGICAL_ERROR, "Only hypothesis index is supported here"); static const NameSet relations = { "equals", "notEquals", "less", "lessOrEquals", "greaterOrEquals", "greater"}; // TODO: move to index hypothesis std::vector compare_hypotheses_data; std::vector hypotheses_data; const auto cnf = TreeCNFConverter::toCNF(hypothesis_index->index.expression_list_ast->children.front()).pullNotOutFunctions(); for (const auto & group : cnf.getStatements()) { if (group.size() == 1) { hypotheses_data.push_back(group); CNFQuery::AtomicFormula atomic_formula = *group.begin(); CNFQuery::AtomicFormula atom{atomic_formula.negative, atomic_formula.ast->clone()}; pushNotIn(atom); assert(!atom.negative); const auto * func = atom.ast->as(); if (func && relations.contains(func->name)) compare_hypotheses_data.push_back(atom.ast); } } index_to_compare_atomic_hypotheses.push_back(compare_hypotheses_data); index_to_atomic_hypotheses.push_back(hypotheses_data); } void MergeTreeIndexhypothesisMergedCondition::addConstraints(const ConstraintsDescription & constraints_description) { auto atomic_constraints_data = constraints_description.getAtomicConstraintData(); for (const auto & atomic_formula : atomic_constraints_data) { CNFQuery::AtomicFormula atom{atomic_formula.negative, atomic_formula.ast->clone()}; pushNotIn(atom); atomic_constraints.push_back(atom.ast); } } /// Replaces < -> <=, > -> >= and assumes that all hypotheses are true then checks if path exists bool MergeTreeIndexhypothesisMergedCondition::alwaysUnknownOrTrue() const { std::vector active_atomic_formulas(atomic_constraints); for (const auto & hypothesis : index_to_compare_atomic_hypotheses) { active_atomic_formulas.insert( std::end(active_atomic_formulas), std::begin(hypothesis), std::end(hypothesis)); } /// transform active formulas for (auto & formula : active_atomic_formulas) { formula = formula->clone(); /// do all operations with copy auto * func = formula->as(); if (func && func->name == "less") func->name = "lessOrEquals"; if (func && func->name == "greater") func->name = "greaterOrEquals"; } const auto weak_graph = std::make_unique(active_atomic_formulas); bool useless = true; expression_cnf->iterateGroups( [&](const CNFQuery::OrGroup & or_group) { for (const auto & atomic_formula : or_group) { CNFQuery::AtomicFormula atom{atomic_formula.negative, atomic_formula.ast->clone()}; pushNotIn(atom); const auto * func = atom.ast->as(); if (func && func->arguments->children.size() == 2) { const auto left = weak_graph->getComponentId(func->arguments->children[0]); const auto right = weak_graph->getComponentId(func->arguments->children[1]); if (left && right && weak_graph->hasPath(left.value(), right.value())) { useless = false; return; } } } }); return useless; } bool MergeTreeIndexhypothesisMergedCondition::mayBeTrueOnGranule(const MergeTreeIndexGranules & granules) const { std::vector values; for (const auto & index_granule : granules) { const auto granule = std::dynamic_pointer_cast(index_granule); if (!granule) throw Exception("Only hypothesis index is supported here.", ErrorCodes::LOGICAL_ERROR); values.push_back(granule->met); } const ComparisonGraph * graph = nullptr; { std::lock_guard lock(cache_mutex); if (const auto it = answer_cache.find(values); it != std::end(answer_cache)) return it->second; graph = getGraph(values); } bool always_false = false; expression_cnf->iterateGroups( [&](const CNFQuery::OrGroup & or_group) { if (always_false) return; for (const auto & atomic_formula : or_group) { CNFQuery::AtomicFormula atom{atomic_formula.negative, atomic_formula.ast->clone()}; pushNotIn(atom); const auto * func = atom.ast->as(); if (func && func->arguments->children.size() == 2) { const auto expected = ComparisonGraph::atomToCompareResult(atom); if (graph->isPossibleCompare(expected, func->arguments->children[0], func->arguments->children[1])) { /// If graph failed use matching. /// We don't need to check constraints. return; } } } always_false = true; }); std::lock_guard lock(cache_mutex); answer_cache[values] = !always_false; return !always_false; } std::unique_ptr MergeTreeIndexhypothesisMergedCondition::buildGraph(const std::vector & values) const { std::vector active_atomic_formulas(atomic_constraints); for (size_t i = 0; i < values.size(); ++i) { if (values[i]) active_atomic_formulas.insert( std::end(active_atomic_formulas), std::begin(index_to_compare_atomic_hypotheses[i]), std::end(index_to_compare_atomic_hypotheses[i])); } return std::make_unique(active_atomic_formulas); } const ComparisonGraph * MergeTreeIndexhypothesisMergedCondition::getGraph(const std::vector & values) const { auto [it, inserted] = graph_cache.try_emplace(values); if (inserted) it->second = buildGraph(values); return it->second.get(); } }