#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, true); } 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("Unknown constraint type.", ErrorCodes::LOGICAL_ERROR); }; 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" }; std::vector 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 std::vector & ConstraintsDescription::getConstraints() const { return constraints; } std::optional ConstraintsDescription::getAtomIds(const ASTPtr & ast) const { const auto hash = ast->getTreeHash(); 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::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(std::vector()); 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()].push_back({i, j}); graph = buildGraph(); } }