/* Copyright (c) 2020 Percona LLC and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MYSQLPP_UDF_WRAPPERS_HPP #define MYSQLPP_UDF_WRAPPERS_HPP #include #include #include #include #include #include #include #include #include #include #ifdef WIN32 #define MYSQLPP_UDF_EXPORT extern "C" __declspec(dllexport) #else #define MYSQLPP_UDF_EXPORT extern "C" #endif namespace mysqlpp { template struct udf_impl_meta_info; template class generic_udf_base { public: static bool init(UDF_INIT *initid, UDF_ARGS *args, char *message) noexcept { udf_context udf_ctx{initid, args}; extended_impl_t *impl = nullptr; try { impl = new extended_impl_t{udf_ctx}; } catch (const std::exception &e) { std::strncpy(message, e.what(), MYSQL_ERRMSG_SIZE); message[MYSQL_ERRMSG_SIZE - 1] = '\0'; return true; } catch (...) { std::strncpy(message, "unexpected exception", MYSQL_ERRMSG_SIZE); message[MYSQL_ERRMSG_SIZE - 1] = '\0'; return true; } initid->ptr = reinterpret_cast(impl); return false; } static void deinit(UDF_INIT *initid) noexcept { delete get_extended_impl_from_udf_initid(initid); } protected: using extended_impl_t = impl_with_mixin, ImplType>; static extended_impl_t *get_extended_impl_from_udf_initid( UDF_INIT *initid) noexcept { return reinterpret_cast(initid->ptr); } static const char *get_function_label(std::string &buffer) noexcept { const char *res = ""; try { buffer = udf_impl_meta_info::name; buffer += '<'; auto item_result_label = get_item_result_label(udf_impl_meta_info::item_result); buffer.append(item_result_label.data(), item_result_label.size()); buffer += '>'; res = buffer.c_str(); } catch (...) { } return res; } static void handle_exception() noexcept { std::string buffer; try { // The following suppression is needed exclusively for Clang 5.0 that // has a bug in noexcept specification diagnostics #if defined(__clang__) && (__clang_major__ == 5) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wexceptions" #endif throw; #if defined(__clang__) && (__clang_major__ == 5) #pragma clang diagnostic pop #endif } catch (const udf_exception &e) { if (e.has_error_code()) my_error(e.get_error_code(), MYF(0), get_function_label(buffer), e.what()); } catch (const std::exception &e) { my_error(ER_UDF_ERROR, MYF(0), get_function_label(buffer), e.what()); } catch (...) { my_error(ER_UDF_ERROR, MYF(0), get_function_label(buffer), "unexpected exception"); } } }; template class generic_udf; template class generic_udf : public generic_udf_base { public: static char *func(UDF_INIT *initid, UDF_ARGS *args, char * /* result */, unsigned long *length, unsigned char *is_null, unsigned char *error) noexcept { auto &extended_impl = *generic_udf_base< ImplType, STRING_RESULT>::get_extended_impl_from_udf_initid(initid); udf_result_t res; const udf_context udf_ctx{initid, args}; try { res = extended_impl.impl.calculate(udf_ctx); } catch (...) { generic_udf_base::handle_exception(); *error = 1; return nullptr; } *error = 0; if (!res) { assert(udf_ctx.is_result_nullabale()); *is_null = 1; return nullptr; } else { *is_null = 0; extended_impl.mixin = std::move(res.get()); *length = extended_impl.mixin.size(); return const_cast(extended_impl.mixin.c_str()); } } }; template class generic_udf : public generic_udf_base { public: static double func(UDF_INIT *initid, UDF_ARGS *args, unsigned char *is_null, unsigned char *error) noexcept { auto &extended_impl = *generic_udf_base< ImplType, REAL_RESULT>::get_extended_impl_from_udf_initid(initid); udf_result_t res; const udf_context udf_ctx{initid, args}; try { res = extended_impl.impl.calculate(udf_ctx); } catch (...) { generic_udf_base::handle_exception(); *error = 1; return 0.0; } *error = 0; if (!res) { assert(udf_ctx.is_result_nullabale()); *is_null = 1; return 0.0; } else { *is_null = 0; return res.get(); } } }; template class generic_udf : public generic_udf_base { public: static double func(UDF_INIT *initid, UDF_ARGS *args, unsigned char *is_null, unsigned char *error) noexcept { auto &extended_impl = *generic_udf_base< ImplType, INT_RESULT>::get_extended_impl_from_udf_initid(initid); udf_result_t res; const udf_context udf_ctx{initid, args}; try { res = extended_impl.impl.calculate(udf_ctx); } catch (...) { generic_udf_base::handle_exception(); *error = 1; return 0.0; } *error = 0; if (!res) { assert(udf_ctx.is_result_nullabale()); *is_null = 1; return 0.0; } else { *is_null = 0; return res.get(); } } }; } // namespace mysqlpp #define DECLARE_UDF_META_INFO(IMPL, RESULT_TYPE, NAME) \ namespace mysqlpp { \ template <> \ struct udf_impl_meta_info { \ static constexpr item_result_type item_result = RESULT_TYPE; \ static constexpr const char *const name = #NAME; \ }; \ } #define DECLARE_UDF_INIT(IMPL, RESULT_TYPE, NAME) \ MYSQLPP_UDF_EXPORT \ bool NAME##_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { \ static_assert(std::is_same::value, \ "Invalid UDF init function signature"); \ return mysqlpp::generic_udf::init(initid, args, \ message); \ } #define DECLARE_UDF_DEINIT(IMPL, RESULT_TYPE, NAME) \ MYSQLPP_UDF_EXPORT \ void NAME##_deinit(UDF_INIT *initid) { \ static_assert( \ std::is_same::value, \ "Invalid UDF deinit function signature"); \ mysqlpp::generic_udf::deinit(initid); \ } #define DECLARE_UDF_STRING_FUNC(IMPL, NAME) \ MYSQLPP_UDF_EXPORT \ char *NAME(UDF_INIT *initid, UDF_ARGS *args, char *result, \ unsigned long *length, unsigned char *is_null, \ unsigned char *error) { \ static_assert(std::is_same::value, \ "Invalid string UDF function signature"); \ return mysqlpp::generic_udf::func( \ initid, args, result, length, is_null, error); \ } #define DECLARE_UDF_REAL_FUNC(IMPL, NAME) \ MYSQLPP_UDF_EXPORT \ double NAME(UDF_INIT *initid, UDF_ARGS *args, unsigned char *is_null, \ unsigned char *error) { \ static_assert(std::is_same::value, \ "Invalid real UDF function signature"); \ return mysqlpp::generic_udf::func(initid, args, \ is_null, error); \ } #define DECLARE_UDF_INT_FUNC(IMPL, NAME) \ MYSQLPP_UDF_EXPORT \ long long NAME(UDF_INIT *initid, UDF_ARGS *args, unsigned char *is_null, \ unsigned char *error) { \ static_assert(std::is_same::value, \ "Invalid int UDF function signature"); \ return mysqlpp::generic_udf::func(initid, args, is_null, \ error); \ } #define DECLARE_STRING_UDF(IMPL, NAME) \ DECLARE_UDF_META_INFO(IMPL, STRING_RESULT, NAME) \ DECLARE_UDF_INIT(IMPL, STRING_RESULT, NAME) \ DECLARE_UDF_STRING_FUNC(IMPL, NAME) \ DECLARE_UDF_DEINIT(IMPL, STRING_RESULT, NAME) #define DECLARE_REAL_UDF(IMPL, NAME) \ DECLARE_UDF_META_INFO(IMPL, REAL_RESULT, NAME) \ DECLARE_UDF_INIT(IMPL, REAL_RESULT, NAME) \ DECLARE_UDF_REAL_FUNC(IMPL, NAME) \ DECLARE_UDF_DEINIT(IMPL, REAL_RESULT, NAME) #define DECLARE_INT_UDF(IMPL, NAME) \ DECLARE_UDF_META_INFO(IMPL, INT_RESULT, NAME) \ DECLARE_UDF_INIT(IMPL, INT_RESULT, NAME) \ DECLARE_UDF_INT_FUNC(IMPL, NAME) \ DECLARE_UDF_DEINIT(IMPL, INT_RESULT, NAME) #endif