#pragma once #include #include #include #include #include #include #include #include namespace DB { template class NamePrompter { public: using DistanceIndex = std::pair; using DistanceIndexQueue = std::priority_queue; static std::vector getHints(const String & name, const std::vector & prompting_strings) { DistanceIndexQueue queue; for (size_t i = 0; i < prompting_strings.size(); ++i) appendToQueue(i, name, queue, prompting_strings); return release(queue, prompting_strings); } private: static size_t levenshteinDistance(const String & lhs, const String & rhs) { size_t m = lhs.size(); size_t n = rhs.size(); PODArrayWithStackMemory row(n + 1); for (size_t i = 1; i <= n; ++i) row[i] = i; for (size_t j = 1; j <= m; ++j) { row[0] = j; size_t prev = j - 1; for (size_t i = 1; i <= n; ++i) { size_t old = row[i]; row[i] = std::min(prev + (std::tolower(lhs[j - 1]) != std::tolower(rhs[i - 1])), std::min(row[i - 1], row[i]) + 1); prev = old; } } return row[n]; } static void appendToQueue(size_t ind, const String & name, DistanceIndexQueue & queue, const std::vector & prompting_strings) { const String & prompt = prompting_strings[ind]; /// Clang SimpleTypoCorrector logic const size_t min_possible_edit_distance = std::abs(static_cast(name.size()) - static_cast(prompt.size())); const size_t mistake_factor = (name.size() + 2) / 3; if (min_possible_edit_distance > 0 && name.size() / min_possible_edit_distance < 3) return; if (prompt.size() <= name.size() + mistake_factor && prompt.size() + mistake_factor >= name.size()) { size_t distance = levenshteinDistance(prompt, name); if (distance <= mistake_factor) { queue.emplace(distance, ind); if (queue.size() > MaxNumHints) queue.pop(); } } } static std::vector release(DistanceIndexQueue & queue, const std::vector & prompting_strings) { std::vector answer; answer.reserve(queue.size()); while (!queue.empty()) { auto top = queue.top(); queue.pop(); answer.push_back(prompting_strings[top.second]); } std::reverse(answer.begin(), answer.end()); return answer; } }; String getHintsErrorMessageSuffix(const std::vector & hints); void appendHintsMessage(String & error_message, const std::vector & hints); template class IHints { public: virtual std::vector getAllRegisteredNames() const = 0; std::vector getHints(const String & name) const { return prompter.getHints(name, getAllRegisteredNames()); } void appendHintsMessage(String & error_message, const String & name) const { auto hints = getHints(name); DB::appendHintsMessage(error_message, hints); } IHints() = default; IHints(const IHints &) = default; IHints(IHints &&) noexcept = default; IHints & operator=(const IHints &) = default; IHints & operator=(IHints &&) noexcept = default; virtual ~IHints() = default; private: NamePrompter prompter; }; }