Uploaded image for project: 'Percona Server for MySQL'
  1. Percona Server for MySQL
  2. PS-7348

Create a set of C++ classes/macros that would simplify the creation of new UDFs

Details

    • Improvement
    • Status: Done
    • Medium
    • Resolution: Done
    • 8.0.x
    • 8.0.22-13
    • None
    • None

    Description

      In order to create a new UDF developer needs to include mysqlpp/udf_wrappers.hpp file.

      #include <mysqlpp/udf_wrappers.hpp>
      

      Developer needs to declare a class with the following requirements:

      • the class must have a constructor that accepts mysqlpp::udf_context
        by non-const reference.
      • the class must have a calculate() method that accepts mysqlpp::udf_context by const reference and returns one of the following:
        • mysqlpp::udf_result_t<STRING_RESULT> (currently, ext::optional<std::string>) for STRING_RESULT UDFs
        • mysqlpp::udf_result_t<REAL_RESULT> (currently, ext::optional<double>) for REAL_RESULT UDFs
        • mysqlpp::udf_result_t<INT_RESULT> (currently, ext::optional<long long>) for INT_RESULT UDFs
        • mysqlpp::udf_result_t<DECIMAL_RESULT> (currently, ext::optional<std::string>) for DECIMAL_RESULT UDFs
      • the class may have a destructor with necessary cleanup logic.

      Developer needs to instantiate a set of c-style functions required by UDF
      (f_init() / f() / f_deinit()) via one of the following macros:

      • DECLARE_STRING_UDF() for STRING_RESULT UDFs
      • DECLARE_REAL_UDF() for REAL_RESULT UDFs
      • DECLARE_INT_UDF() for INT_RESULT UDFs
      • DECLARE_DECIMAL_UDF() for DECIMAL_RESULT UDFs

      For instance,

      class blah_impl {
        public:
          blah_impl(mysqlpp::udf_context& ctx) {
            // we expect the number of arguments to be exactly 1
            if (ctx.get_number_of_args() != 1)
              throw std::invalid_argument(
                  "BLAH() requires exactly one argument");
            // specify whether function always return the same value
            ctx.mark_result_const(false);
            // specify whether function result may be null
            ctx.mark_result_nullable(false);
            // we do not expect the first argument to be null
            ctx.mark_arg_nullable(0, false);
            // we expect the first argument to be of string type
            ctx.set_arg_type(0, STRING_RESULT);
          }
          mysqlpp::udf_result_t<STRING_RESULT> calculate(const mysqlpp::udf_context &ctx) {
            return "modified-" + ctx.get_arg<STRING_RESULT>(0);
          }
      }
      DECLARE_STRING_UDF(blah_impl, blah)
      

      Create and use the UDF as usually

      CREATE FUNCTION blah RETURNS STRING SONAME "libblah.so";
      SELECT blah('foo') AS result;
      +--------------+
      | result       |
      +--------------+
      | modified-foo |
      +--------------+
      DROP FUNCTION blah;
      

      Developer is allowed to throw exceptions from both the _impl class constructor and from the calculate() method in indicate errors.

      • Exception e derived from std::exception thrown from the constructor will be translated into ER_CANT_INITIALIZE_UDF MySQL error with the custom message e.what()
        ERROR HY000: Can't initialize function 'wrapped_udf_string'; function requires exactly one argument
        
      • Exception e not derived from std::exception thrown from the constructor will be translated into ER_CANT_INITIALIZE_UDF MySQL error with the predefined error message unexpected exception
        ERROR HY000: Can't initialize function 'wrapped_udf_string'; unexpected exception
        
      • Exception e derived from std::exception thrown from the calculate() method will be translated into ER_UDF_ERROR MySQL error with the custom message e.what()
        ERROR HY000: <function_name> UDF failed; test runtime_error
        
      • Exception e not derived from std::exception thrown from the calculate() method will be translated into ER_UDF_ERROR MySQL error with the predefined error message unexpected exception
        ERROR HY000: <function_name> UDF failed; unexpected exception
        
      • Exception e derived from mysqlpp::udf_exception thrown from the calculate() method will be translated into e.get_error_code() MySQL error with the custom message e.what()
        ERROR HY000: Wrapped UDF exception in function '<function_name>'; test udf_exception without sentinel
        
        • Please notice that custom error code supplied to mysqlpp::udf_exception constructor (when an instance of this exception is thrown) must correspond to a string resource with exactly two %s parameters (one for the function name
          and another one for the custom message)
        • mysqlpp::udf_exception exceptions constructed without error code (with single string only), result in not calling my_error() at all. This construct can be used in cases when one of the internal MySQL functions already set the error code and developer does not want to overwrite it.
        • Predefined ER_WRAPPED_UDF_EXCEPTION custom error code can be used to signal general purpose errors from the wrapped UDFs
          throw mysqlpp::udf_exception("<message>", ER_WRAPPED_UDF_EXCEPTION);
          

      Attachments

        Issue Links

          Activity

            People

              yura.sorokin Yura Sorokin
              yura.sorokin Yura Sorokin
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Time Tracking

                  Estimated:
                  Original Estimate - Not Specified
                  Not Specified
                  Remaining:
                  Remaining Estimate - Not Specified
                  Not Specified
                  Logged:
                  Time Spent - 1 week, 6 hours
                  1w 6h

                  Smart Checklist