WL#8177: SQL service API

Affects: Server-5.7   —   Status: Complete

Goal of this WL is to allow to run SQL queries and get the result in the current
thread or obtained from Session service (WL#7947).

Note. Due to limitation of underlying work (WL#7126) execution of prepared
statements won't be supported. When that limitation would be lifted, PS will
automatically become available via this service.
The goal fo this WL is to provide a service to allow plugins to
run server commands, SQL queries among them.

This WL consists of several parts:

1) A service to run commands
There are two parts of it: one handles the protocol through which
server interacts with the plugin and another actually runs commands.

struct my_cmd_service
{
  /*
    Initialize protocol and returns its handler on success

    @param protocol_cbs  Struct of callbacks to interact with server
    @param ctx           Plugin's context, an opaque pointer that will be
                         passed to callbacks. Might be NULL.
  */
  MYSQL_PROTOCOL (*init_protocol)(const struct protocol_cbs *cbs, void *ctx);

  /*
    Free protocol

    @param protocol Protocol handler to free

    @details Frees protocol, its handler becomes invalid, attempt to use it
    would likely result in a crash.
  */
  void (*free_protocol)(MYSQL_PROTOCOL protocol);

  /*
    Execute a server command.

    @param thd      Session handler obtained via Session service
    @param protocol Protocol handler
    @param command  Command needs to be executed
    @param data     Command's arguments

    @returns
      true  an error occured
      false success
  */
  int (*run_command)(MYSQL_THD thd, MYSQL_PROTOCOL protocol
                      enum_server_command command, COM_DATA *data);
};

A plugin might allocate as many protocols with different callback functions as
it needs, e.g one protocol with different callback functions and context for
each command it wants to run.

Having obtained thd and protocol a plugin can run commands by means of the
run_command() function, which is the service's wrapper for do_command()
function. After it's called, the plugin should provide server,
via callback functions, the command it wants to run and arguments to it.
Here is an example session of a COM_QUERY with "SELECT ..." query:

          Server                |         Plugin
--------------------------------+-----------------------------
                                | calls run_command()
run_command()                   |           
  switches thd and protocol     |
server's dispatch_command()     |
  calls callbacks:              |
                        ... execution ...
                                |
                     ... sending metadata ...
                                | start_result_metadata(...)
                                | field_metadata(...)
                 ... same for each field sent ...
                                | end_result_metadata(...)
                                |
                     ... sending result ...
                                | start_row(...)
                                | get_xxx(...)
      ... same for each field, xxx is type according to metadata ...
                                | end_row(...)
           ... same for each row, until all rows are sent ...
                                | handle_ok(...)
                                |
run_command() returns           |


2) Struct holding callback functions for interaction with server. It consists
of several logical parts:
  1) getting metadata from server
  2) getting data from server
  3) getting execution status 

struct sql_protocol_cbs
{
  /*** Getting metadata ***/
  /*
    Indicates beginning of metadata for the result set

    @param ctx      Plugin's context
    @param num_cols Number of fields being sent
    @param resultcs Charset of the result set

    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*start_result_metadata)(void *ctx, uint num_cols,
                                const CHARSET_INFO *resultcs);
  /*
    Field metadata is provided via this callback

    @param ctx     Plugin's context
    @param field   Field's metadata (see field.h)
    @param charset Field's charset

    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*field_metadata)(void *ctx, Send_field *field,
                         const CHARSET_INFO *charset);
  
  /*
    Indicates end of metadata for the result set

    @param ctx     Plugin's context
    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*end_result_metadata)(void *ctx);

  /*
    Indicates the beginning of a new row in the result set

    @param ctx   Plugin's context

    @returns
      true  an error occured, server will abort the command
      false ok

  */
  int (*start_row)(void *ctx);

  /*
    Indicates the end of the current row in the result set

    @param ctx   Plugin's context

    @returns
      true  an error occured, server will abort the command
      false ok

  */
  int (*end_row)(void *ctx);

  /*
    An error occured during execution

    @details This callback indicates that an error occureded during command
    execution and the partial row should be dropped. Server will raise error
    and return.

    @param ctx   Plugin's context

    @returns
      true  an error occured, server will abort the command
      false ok

  */
  void (*abort_row)(void *ctx);
  
  /*
    Return client's capabilities (see mysql_com.h, CLIENT_*)

    @param ctx     Plugin's context
    
    @return Bitmap of client's capabilities
  */
  ulonglong (*get_client_caps)(void *ctx);

  /****** Getting data ******/
  /*
    Receive NULL value from server

    @param ctx  Plugin's context

    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*get_null)(void * ctx);

  /*
    Get TINY/SHORT/LONG value from server

    @param ctx           Plugin's context
    @param value         Value received

    @note In order to know which type exactly was received, the plugin must
    track the metadata that was sent just prior to the result set.

    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*get_integer)(void * ctx, longlong value);

  /*
    Get LONGLONG value from server

    @param ctx           Plugin's context
    @param value         Value received
    @param unsigned_flag TRUE <=> value is unsigned

    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*get_longlong)(void * ctx, longlong value, bool unsigned_flag);

  /*
    Receive DECIMAL value from server

    @param ctx   Plugin's context
    @param value Value received

    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*get_decimal)(void * ctx, const my_decimal * value);

  /*
    Get FLOAT/DOUBLE from server

    @param ctx      Plugin's context
    @param value    Value received
    @param decimals Number of decimals

    @note In order to know which type exactly was received, the plugin must
    track the metadata that was sent just prior to the result set.

    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*get_double)(void * ctx, double value, uint32 decimals);

  /*
    Get DATE value from server

    @param ctx      Plugin's context
    @param value    Value received

    @returns
      true  an error occured during storing, server will abort the command
      false ok
  */
  int (*get_date)(void * ctx, const MYSQL_TIME * value);

  /*
    Get TIME value from server

    @param ctx      Plugin's context
    @param value    Value received
    @param decimals Number of decimals

    @returns
      true  an error occured during storing, server will abort the command
      false ok
  */
  int (*get_time)(void * ctx, const MYSQL_TIME * value, uint decimals);

  /*
    Get DATETIME value from server

    @param ctx      Plugin's context
    @param value    Value received
    @param decimals Number of decimals

    @returns
      true  an error occured during storing, server will abort the command
      false ok
  */
  int (*get_datetime)(void * ctx, const MYSQL_TIME * value, uint decimals);

  /*
    Get STRING value from server

    @param ctx     Plugin's context
    @param value   Value received
    @param length  Value's length
    @param valuecs Value's charset

    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*get_string)(void * ctx,
                     const char * const value, size_t length,
                     const CHARSET_INFO * const valuecs);

  /*
    Get field text or binary from server

    @param ctx   Plugin's context
    @param field Proto_field received (see WL#7126)

    @details This method should call either Proto_field::send_text() or
    Proto_field::send_binary, depending on the desired result. Field will
    send itself in either text of binary form by calling one of get_xxx()
    callback functions above.

    @returns
      true  an error occured, server will abort the command
      false ok
  */
  int (*get_field)(void *ctx, Proto_field *field);

  /****** Getting execution status ******/
  /*
    Command ended with success

    @param ctx                  Plugin's context
    @param server_status        Status of server (see mysql_com.h,
                                SERVER_STATUS_*)
    @param statement_warn_count Number of warnings thrown during execution
    @param affected_rows        Number of rows affected by the command
    @param last_insert_id       Last insert id being assigned during execution
    @param message              A message from server

  */
  void (*handle_ok)(void * ctx,
                    uint server_status, uint statement_warn_count,
                    ulonglong affected_rows, ulonglong last_insert_id,
                    const char * const message);
  /*
    Command ended with ERROR

    @param ctx       Plugin's context
    @param sql_errno Error code
    @param err_msg   Error message
    @param sqlstate  SQL state correspongin to the error code

  */
  void (*handle_error)(void * ctx, uint sql_errno, const char * const err_msg,
                       const char * const sqlstate);
  /*
    Session was shutdown while command was running

    @param ctx  Plugin's context
    
  */
  void (*shutdown)(void *ctx);

  /*
    Checks if the connection is functioning

    @param ctx  Plugin's context

    @return 
      1  alive
      0  dead
  */
  int (*connection_alive)(void *ctx);
}

All callback functions are plain mapping to Protocol's actual methods.
Callback pointers might be NULL, in such case they will be ignored.

Since the service would use the provided protocol, handle_ok/handle_error
callback functions effectively will overload ones provided for session service.
It's up to the plugin whether to use same or different functions for
handle_error/handle_ok callback functions.
Dealing with Protocol
=====================
In order to communicate with caller plugin, a new class Protocol_callback is
introduced. It's derived from Protocol. This class is a mere proxy, passing
all its methods to appropriate callbacks provided at initialization, and isn't
visible to the plugin. 
Exceptions to 1:1 method-callback mapping:
.) store_tiny(), store_short(), store_long() are all calling get_integer()
.) store_float(), store_double() are both calling get_double()
.) get_client_caps() should cache plugin's capabilities, has_client_cap()
should use that cache instead of asking the plugin.
.) send_eof() should call handle_ok().


Command execution
=================
Prior executing a command the service should do following:
1) Ensure that THD provided exists in server's thread list. Returns error
otherwise.
2) For non-daemon plugins backup current_thd
3) Set THD provided by plugin
4) Backup default protocol and set plugin's one

To actually execute the command, dispatch_command() is called.

After returning from do_command() service should rollback changes it made:
1) restore default protocol
2) for non-daemon plugins - restore original current_thd