#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DB { namespace ErrorCodes { extern const int LOGICAL_ERROR; } String ConstraintsDescription::toString() const { if (constraints.empty()) return {}; ASTExpressionList list; for (const auto & constraint : constraints) list.children.push_back(constraint); return serializeAST(list); } ConstraintsDescription ConstraintsDescription::parse(const String & str) { if (str.empty()) return {}; ConstraintsDescription res; ParserConstraintDeclarationList parser; ASTPtr list = parseQuery(parser, str, 0, DBMS_DEFAULT_MAX_PARSER_DEPTH); for (const auto & constraint : list->children) res.constraints.push_back(constraint); return res; } ASTs ConstraintsDescription::filterConstraints(ConstraintType selection) const { const auto ast_to_decr_constraint_type = [](ASTConstraintDeclaration::Type constraint_type) -> UInt8 { switch (constraint_type) { case ASTConstraintDeclaration::Type::CHECK: return static_cast(ConstraintType::CHECK); case ASTConstraintDeclaration::Type::ASSUME: return static_cast(ConstraintType::ASSUME); } throw Exception(ErrorCodes::LOGICAL_ERROR, "Unknown constraint type."); }; ASTs res; res.reserve(constraints.size()); for (const auto & constraint : constraints) { if ((ast_to_decr_constraint_type(constraint->as()->type) & static_cast(selection)) != 0) { res.push_back(constraint); } } return res; } std::vector> ConstraintsDescription::buildConstraintData() const { std::vector> constraint_data; for (const auto & constraint : filterConstraints(ConstraintsDescription::ConstraintType::ALWAYS_TRUE)) { const auto cnf = TreeCNFConverter::toCNF(constraint->as()->expr->ptr()) .pullNotOutFunctions(); /// TODO: move prepare stage to ConstraintsDescription for (const auto & group : cnf.getStatements()) constraint_data.emplace_back(std::begin(group), std::end(group)); } return constraint_data; } std::vector ConstraintsDescription::getAtomicConstraintData() const { std::vector constraint_data; for (const auto & constraint : filterConstraints(ConstraintsDescription::ConstraintType::ALWAYS_TRUE)) { const auto cnf = TreeCNFConverter::toCNF(constraint->as()->expr->ptr()) .pullNotOutFunctions(); for (const auto & group : cnf.getStatements()) { if (group.size() == 1) constraint_data.push_back(*group.begin()); } } return constraint_data; } std::unique_ptr> ConstraintsDescription::buildGraph() const { static const NameSet relations = { "equals", "less", "lessOrEquals", "greaterOrEquals", "greater" }; ASTs constraints_for_graph; auto atomic_formulas = getAtomicConstraintData(); for (const auto & atomic_formula : atomic_formulas) { CNFQuery::AtomicFormula atom{atomic_formula.negative, atomic_formula.ast->clone()}; pushNotIn(atom); auto * func = atom.ast->as(); if (func && relations.contains(func->name)) { assert(!atom.negative); constraints_for_graph.push_back(atom.ast); } } return std::make_unique>(constraints_for_graph); } ConstraintsExpressions ConstraintsDescription::getExpressions(const DB::ContextPtr context, const DB::NamesAndTypesList & source_columns_) const { ConstraintsExpressions res; res.reserve(constraints.size()); for (const auto & constraint : constraints) { auto * constraint_ptr = constraint->as(); if (constraint_ptr->type == ASTConstraintDeclaration::Type::CHECK) { // TreeRewriter::analyze has query as non-const argument so to avoid accidental query changes we clone it ASTPtr expr = constraint_ptr->expr->clone(); auto syntax_result = TreeRewriter(context).analyze(expr, source_columns_); res.push_back(ExpressionAnalyzer(constraint_ptr->expr->clone(), syntax_result, context).getActions(false, true, CompileExpressions::yes)); } } return res; } const ComparisonGraph & ConstraintsDescription::getGraph() const { return *graph; } const std::vector> & ConstraintsDescription::getConstraintData() const { return cnf_constraints; } const ASTs & ConstraintsDescription::getConstraints() const { return constraints; } std::optional ConstraintsDescription::getAtomIds(const ASTPtr & ast) const { const auto hash = ast->getTreeHash(/*ignore_aliases=*/ true); auto it = ast_to_atom_ids.find(hash); if (it != ast_to_atom_ids.end()) return it->second; return std::nullopt; } std::vector ConstraintsDescription::getAtomsById(const ConstraintsDescription::AtomIds & ids) const { std::vector result; for (const auto & id : ids) result.push_back(cnf_constraints[id.group_id][id.atom_id]); return result; } ConstraintsDescription::QueryTreeData ConstraintsDescription::getQueryTreeData(const ContextPtr & context, const QueryTreeNodePtr & table_node) const { QueryTreeData data; std::vector atomic_constraints_data; QueryAnalysisPass pass(table_node); for (const auto & constraint : filterConstraints(ConstraintsDescription::ConstraintType::ALWAYS_TRUE)) { auto query_tree = buildQueryTree(constraint->as()->expr->ptr(), context); pass.run(query_tree, context); const auto cnf = Analyzer::CNF::toCNF(query_tree, context) .pullNotOutFunctions(context); for (const auto & group : cnf.getStatements()) { data.cnf_constraints.emplace_back(group.begin(), group.end()); if (group.size() == 1) atomic_constraints_data.emplace_back(*group.begin()); } data.constraints.push_back(std::move(query_tree)); } for (size_t i = 0; i < data.cnf_constraints.size(); ++i) for (size_t j = 0; j < data.cnf_constraints[i].size(); ++j) data.query_node_to_atom_ids[data.cnf_constraints[i][j].node_with_hash].push_back({i, j}); /// build graph if (constraints.empty()) { data.graph = std::make_unique>(QueryTreeNodes(), context); } else { static const NameSet relations = { "equals", "less", "lessOrEquals", "greaterOrEquals", "greater" }; QueryTreeNodes constraints_for_graph; for (const auto & atomic_formula : atomic_constraints_data) { Analyzer::CNF::AtomicFormula atom{atomic_formula.negative, atomic_formula.node_with_hash.node->clone()}; atom = Analyzer::CNF::pushNotIntoFunction(atom, context); auto * function_node = atom.node_with_hash.node->as(); if (function_node && relations.contains(function_node->getFunctionName())) { assert(!atom.negative); constraints_for_graph.push_back(atom.node_with_hash.node); } } data.graph = std::make_unique>(constraints_for_graph, context); } return data; } const QueryTreeNodes & ConstraintsDescription::QueryTreeData::getConstraints() const { return constraints; } const std::vector> & ConstraintsDescription::QueryTreeData::getConstraintData() const { return cnf_constraints; } const ComparisonGraph & ConstraintsDescription::QueryTreeData::getGraph() const { return *graph; } std::optional ConstraintsDescription::QueryTreeData::getAtomIds(const QueryTreeNodePtrWithHash & node_with_hash) const { auto it = query_node_to_atom_ids.find(node_with_hash); if (it != query_node_to_atom_ids.end()) return it->second; return std::nullopt; } std::vector ConstraintsDescription::QueryTreeData::getAtomsById(const AtomIds & ids) const { std::vector result; for (const auto & id : ids) result.push_back(cnf_constraints[id.group_id][id.atom_id]); return result; } ConstraintsDescription::ConstraintsDescription(const ASTs & constraints_) : constraints(constraints_) { update(); } ConstraintsDescription::ConstraintsDescription(const ConstraintsDescription & other) { constraints.reserve(other.constraints.size()); for (const auto & constraint : other.constraints) constraints.emplace_back(constraint->clone()); update(); } ConstraintsDescription & ConstraintsDescription::operator=(const ConstraintsDescription & other) { constraints.resize(other.constraints.size()); for (size_t i = 0; i < constraints.size(); ++i) constraints[i] = other.constraints[i]->clone(); update(); return *this; } ConstraintsDescription::ConstraintsDescription(ConstraintsDescription && other) noexcept : constraints(std::move(other.constraints)) { update(); } ConstraintsDescription & ConstraintsDescription::operator=(ConstraintsDescription && other) noexcept { constraints = std::move(other.constraints); update(); return *this; } void ConstraintsDescription::update() { if (constraints.empty()) { cnf_constraints.clear(); ast_to_atom_ids.clear(); graph = std::make_unique>(ASTs()); return; } cnf_constraints = buildConstraintData(); ast_to_atom_ids.clear(); for (size_t i = 0; i < cnf_constraints.size(); ++i) for (size_t j = 0; j < cnf_constraints[i].size(); ++j) ast_to_atom_ids[cnf_constraints[i][j].ast->getTreeHash(/*ignore_aliases=*/ true)].push_back({i, j}); graph = buildGraph(); } }