/* Copyright (c) 2015, Oracle 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 */

/**
  @addtogroup Replication
  @{

  @file

  @brief Transaction boundary parser definitions. This includes code for
  parsing a stream of events identifying the transaction boundaries (like
  if the event is starting a transaction, is in the middle of a transaction
  or if the event is ending a transaction).
*/

#ifndef RPL_TRX_BOUNDARY_PARSER_H
#define RPL_TRX_BOUNDARY_PARSER_H

#include "my_global.h"

class Format_description_log_event;

/**
  @class Transaction_boundary_parser

  This is the base class for verifying transaction boundaries in a replication
  event stream.
*/
class Transaction_boundary_parser
{
public:
  /**
     Constructor.
  */
  Transaction_boundary_parser()
    :current_parser_state(EVENT_PARSER_NONE)
  {
    DBUG_ENTER("Transaction_boundary_parser::Transaction_boundary_parser");
    DBUG_VOID_RETURN;
  }

  /**
     Reset the transaction boundary parser state.
  */
  void reset();

  /*
    In an event stream, an event is considered safe to be separated from the
    next if it is not inside a transaction.
    We need to know this in order to evaluate if we will let the relay log
    to be rotated or not.
  */

  /**
     State if the transaction boundary parser is inside a transaction.
     This "inside a transaction" means that the parser was fed with at least
     one event of a transaction, but the transaction wasn't completely fed yet.
     This also means that the last event fed depends on following event(s) to
     be correctly applied.

     @return  false if the boundary parser is not inside a transaction.
              true if the boundary parser is inside a transaction.
  */
  inline bool is_inside_transaction()
  {
    return (current_parser_state != EVENT_PARSER_ERROR &&
            current_parser_state != EVENT_PARSER_NONE);
  }

  /**
     State if the transaction boundary parser is not inside a transaction.
     This "not inside a transaction" means that the parser was fed with an
     event that doesn't depend on following events.

     @return  false if the boundary parser is inside a transaction.
              true if the boundary parser is not inside a transaction.
  */
  inline bool is_not_inside_transaction()
  {
    return (current_parser_state == EVENT_PARSER_NONE);
  }

  /**
     State if the transaction boundary parser was fed with a sequence of events
     that the parser wasn't able to parse correctly.

     @return  false if the boundary parser is not in the error state.
              true if the boundary parser is in the error state.
  */
  inline bool is_error()
  {
    return (current_parser_state == EVENT_PARSER_ERROR);
  }

  /**
     Feed the transaction boundary parser with a Log_event of any type,
     serialized into a char* buffer.

     @param buf            Pointer to the event buffer.
     @param length         The size of the event buffer.
     @param fd_event       The description event of the master which logged
                           the event.
     @param throw_warnings If the function should throw warning messages while
                           updating the boundary parser state.
                           While initializing the Relay_log_info the
                           relay log is scanned backwards and this could
                           generate false errors. So, in this case, we
                           don't want to throw warnings.

     @return  false if the transaction boundary parser accepted the event.
              true if the transaction boundary parser didn't accepted the event.
  */
  bool feed_event(const char *buf, size_t length,
                  const Format_description_log_event *description_event,
                  bool throw_warnings);

private:
  enum enum_event_boundary_type {
    EVENT_BOUNDARY_TYPE_ERROR= -1,
    /* Gtid_log_event */
    EVENT_BOUNDARY_TYPE_GTID= 0,
    /* Query_log_event(BEGIN), Query_log_event(XA START) */
    EVENT_BOUNDARY_TYPE_BEGIN_TRX= 1,
    /* Xid, Query_log_event(COMMIT), Query_log_event(ROLLBACK), XA_Prepare_log_event */
    EVENT_BOUNDARY_TYPE_END_TRX= 2,
    /* Query_log_event(XA ROLLBACK) */
    EVENT_BOUNDARY_TYPE_END_XA_TRX= 3,
    /* User_var, Intvar and Rand */
    EVENT_BOUNDARY_TYPE_PRE_STATEMENT= 4,
    /*
      All other Query_log_events and all other DML events
      (Rows, Load_data, etc.)
    */
    EVENT_BOUNDARY_TYPE_STATEMENT= 5,
    /*
      All non DDL/DML events: Format_desc, Rotate, Incident,
      Previous_gtids, Stop, etc.
    */
    EVENT_BOUNDARY_TYPE_IGNORE= 6
  };

  /*
    Internal states for parsing a stream of events.

    DDL has the format:
      DDL-1: [GTID]
      DDL-2: [User] [Intvar] [Rand]
      DDL-3: Query

    DML has the format:
      DML-1: [GTID]
      DML-2: Query(BEGIN)
      DML-3: Statements
      DML-4: (Query(COMMIT) | Query([XA] ROLLBACK) | Xid | Xa_prepare)
  */
  enum enum_event_parser_state {
    /* NONE is set after DDL-3 or DML-4 */
    EVENT_PARSER_NONE,
    /* GTID is set after DDL-1 or DML-1 */
    EVENT_PARSER_GTID,
    /* DDL is set after DDL-2 */
    EVENT_PARSER_DDL,
    /* DML is set after DML-2 */
    EVENT_PARSER_DML,
    /* ERROR is set whenever the above pattern is not followed */
    EVENT_PARSER_ERROR
  };

  /**
     Current internal state of the event parser.
  */
  enum_event_parser_state current_parser_state;

  /**
     Parses an event based on the event parser logic.
  */
  static enum_event_boundary_type
  get_event_boundary_type(
    const char *buf, size_t length,
    const Format_description_log_event *description_event,
    bool throw_warnings);

  /**
     Set the boundary parser state based on the event parser logic.
  */
  bool update_state(enum_event_boundary_type event_boundary_type,
                    bool throw_warnings);
};

/**
  @} (End of group Replication)
*/

#endif /* RPL_TRX_BOUNDARY_PARSER_H */
