/* Copyright (c) 2014, 2024, Oracle and/or its affiliates.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License, version 2.0,
   as published by the Free Software Foundation.

   This program is designed to work with certain software (including
   but not limited to OpenSSL) that is licensed under separate terms,
   as designated in a particular file or component or in included license
   documentation.  The authors of MySQL hereby grant you an additional
   permission to link the program and your derivative works with the
   separately licensed software that they have either included with
   the program or referenced in the documentation.

   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, version 2.0, 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 */

/**
  @file rows_event.h

  @brief Contains the classes representing events which are used for row based
  replication. In row-based replication, the master writes events to the binary
  log that indicate how individual table rows are changed.
*/

#ifndef MYSQL_BINLOG_EVENT_ROWS_EVENT_H
#define MYSQL_BINLOG_EVENT_ROWS_EVENT_H

#include <sstream>
#include <vector>
#include "mysql/binlog/event/control_events.h"
#include "mysql/binlog/event/table_id.h"

/// @addtogroup GroupLibsMysqlBinlogEvent
/// @{

/**
   1 byte length, 1 byte format
   Length is total length in bytes, including 2 byte header
   Length values 0 and 1 are currently invalid and reserved.
*/
#define EXTRA_ROW_INFO_LEN_OFFSET 0
#define EXTRA_ROW_INFO_FORMAT_OFFSET 1
#define EXTRA_ROW_INFO_HEADER_LENGTH 2
#define EXTRA_ROW_INFO_MAX_PAYLOAD (255 - EXTRA_ROW_INFO_HEADER_LENGTH)

#define ROWS_MAPID_OFFSET 0
#define ROWS_FLAGS_OFFSET 6
#define ROWS_VHLEN_OFFSET 8
#define EXTRA_ROW_INFO_TYPECODE_LENGTH 1
#define EXTRA_ROW_PART_INFO_VALUE_LENGTH 2

/**
  This is the typecode defined for the different elements present in
  the container Extra_row_info, this is different from the format information
  stored inside extra_row_ndb_info at EXTRA_ROW_INFO_FORMAT_OFFSET.
*/
enum class enum_extra_row_info_typecode { NDB = 0, PART = 1 };

namespace mysql::binlog::event {
/**
  @class Table_map_event

  In row-based mode, every row operation event is preceded by a
  Table_map_event which maps a table definition to a number.  The
  table definition consists of database name, table name, and column
  definitions.

  @section Table_map_event_binary_format Binary Format

  The Post-Header has the following components:

  <table>
  <caption>Post-Header for Table_map_event</caption>

  <tr>
    <th>Name</th>
    <th>Format</th>
    <th>Description</th>
  </tr>

  <tr>
    <td>table_id</td>
    <td>6 bytes unsigned integer</td>
    <td>The number that identifies the table.</td>
  </tr>

  <tr>
    <td>flags</td>
    <td>2 byte bitfield</td>
    <td>Reserved for future use; currently always 0.</td>
  </tr>

  </table>

  The Body has the following components:

  <table>
  <caption>Body for Table_map_event</caption>

  <tr>
    <th>Name</th>
    <th>Format</th>
    <th>Description</th>
  </tr>

  <tr>
    <td>database_name</td>
    <td>one byte string length, followed by null-terminated string</td>
    <td>The name of the database in which the table resides.  The name
    is represented as a packed variable-length integer representing the
    number of bytes in the name, followed by length bytes containing
    the database name, followed by a terminating 0 byte.  (Note the
    redundancy in the representation of the length.)  </td>
  </tr>

  <tr>
    <td>table_name</td>
    <td> The table name is represented as a packed variable-length integer
    representing the number of bytes in the name, followed by null-terminated
    string</td>
    <td>The name of the table, encoded the same way as the database
    name above.</td>
  </tr>

  <tr>
    <td>column_count</td>
    <td>@ref packed_integer "Packed Integer"</td>
    <td>The number of columns in the table, represented as a packed
    variable-length integer.</td>
  </tr>

  <tr>
    <td>column_type</td>
    <td>List of column_count 1 byte enumeration values</td>
    <td>The type of each column in the table, listed from left to
    right.  Each byte is mapped to a column type according to the
    enumeration type enum_field_types defined in mysql_com.h.  The
    mapping of types to numbers is listed in the table @ref
    Table_table_map_event_column_types "below" (along with
    description of the associated metadata field).  </td>
  </tr>

  <tr>
    <td>metadata_length</td>
    <td>@ref packed_integer "Packed Integer"</td>
    <td>The length of the following metadata block</td>
  </tr>

  <tr>
    <td>metadata</td>
    <td>list of metadata for each column</td>
    <td>For each column from left to right, a chunk of data who's
    length and semantics depends on the type of the column.  The
    length and semantics for the metadata for each column are listed
    in the table @ref Table_table_map_event_column_types
    "below".</td>
  </tr>

  <tr>
    <td>null_bits</td>
    <td>column_count bits, rounded up to nearest byte</td>
    <td>For each column, a bit indicating whether data in the column
    can be NULL or not.  The number of bytes needed for this is
    int((column_count + 7) / 8).  The flag for the first column from the
    left is in the least-significant bit of the first byte, the second
    is in the second least significant bit of the first byte, the
    ninth is in the least significant bit of the second byte, and so
    on.  </td>
  </tr>

  <tr>
    <td>optional metadata fields</td>
    <td>optional metadata fields are stored in Type, Length, Value(TLV) format.
    Type takes 1 byte. Length is a packed integer value. Values takes
    Length bytes.
    </td>
    <td>There are some optional metadata defined. They are listed in the table
    @ref Table_table_map_event_optional_metadata. Optional metadata fields
    follow null_bits. Whether binlogging an optional metadata is decided by the
    server. The order is not defined, so they can be binlogged in any order.
    </td>
  </tr>
  </table>

  The table below lists all column types, along with the numerical
  identifier for it and the size and interpretation of meta-data used
  to describe the type.

  @anchor Table_table_map_event_column_types
  <table>
  <caption>Table_map_event column types: numerical identifier and
  metadata</caption>
  <tr>
    <th>Name</th>
    <th>Identifier</th>
    <th>Size of metadata in bytes</th>
    <th>Description of metadata</th>
  </tr>

  <tr>
    <td>MYSQL_TYPE_DECIMAL</td><td>0</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_TINY</td><td>1</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_SHORT</td><td>2</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_LONG</td><td>3</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_FLOAT</td><td>4</td>
    <td>1 byte</td>
    <td>1 byte unsigned integer, representing the "pack_length", which
    is equal to sizeof(float) on the server from which the event
    originates.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_DOUBLE</td><td>5</td>
    <td>1 byte</td>
    <td>1 byte unsigned integer, representing the "pack_length", which
    is equal to sizeof(double) on the server from which the event
    originates.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_NULL</td><td>6</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_TIMESTAMP</td><td>7</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_LONGLONG</td><td>8</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_INT24</td><td>9</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_DATE</td><td>10</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_TIME</td><td>11</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_DATETIME</td><td>12</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_YEAR</td><td>13</td>
    <td>0</td>
    <td>No column metadata.</td>
  </tr>

  <tr>
    <td><i>MYSQL_TYPE_NEWDATE</i></td><td><i>14</i></td>
    <td>&ndash;</td>
    <td><i>This enumeration value is only used internally and cannot
    exist in a binlog.</i></td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_VARCHAR</td><td>15</td>
    <td>2 bytes</td>
    <td>2 byte unsigned integer representing the maximum length of
    the string.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_BIT</td><td>16</td>
    <td>2 bytes</td>
    <td>A 1 byte unsigned int representing the length in bits of the
    bitfield (0 to 64), followed by a 1 byte unsigned int
    representing the number of bytes occupied by the bitfield.  The
    number of bytes is either int((length + 7) / 8) or int(length / 8).
    </td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_NEWDECIMAL</td><td>246</td>
    <td>2 bytes</td>
    <td>A 1 byte unsigned int representing the precision, followed
    by a 1 byte unsigned int representing the number of decimals.</td>
  </tr>

  <tr>
    <td><i>MYSQL_TYPE_ENUM</i></td><td><i>247</i></td>
    <td>&ndash;</td>
    <td><i>This enumeration value is only used internally and cannot
    exist in a binlog.</i></td>
  </tr>

  <tr>
    <td><i>MYSQL_TYPE_SET</i></td><td><i>248</i></td>
    <td>&ndash;</td>
    <td><i>This enumeration value is only used internally and cannot
    exist in a binlog.</i></td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_TINY_BLOB</td><td>249</td>
    <td>&ndash;</td>
    <td><i>This enumeration value is only used internally and cannot
    exist in a binlog.</i></td>
  </tr>

  <tr>
    <td><i>MYSQL_TYPE_MEDIUM_BLOB</i></td><td><i>250</i></td>
    <td>&ndash;</td>
    <td><i>This enumeration value is only used internally and cannot
    exist in a binlog.</i></td>
  </tr>

  <tr>
    <td><i>MYSQL_TYPE_LONG_BLOB</i></td><td><i>251</i></td>
    <td>&ndash;</td>
    <td><i>This enumeration value is only used internally and cannot
    exist in a binlog.</i></td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_BLOB</td><td>252</td>
    <td>1 byte</td>
    <td>The pack length, i.e., the number of bytes needed to represent
    the length of the blob: 1, 2, 3, or 4.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_VAR_STRING</td><td>253</td>
    <td>2 bytes</td>
    <td>This is used to store both strings and enumeration values.
    The first byte is a enumeration value storing the <i>real
    type</i>, which may be either MYSQL_TYPE_VAR_STRING or
    MYSQL_TYPE_ENUM.  The second byte is a 1 byte unsigned integer
    representing the field size, i.e., the number of bytes needed to
    store the length of the string.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_STRING</td><td>254</td>
    <td>2 bytes</td>
    <td>The first byte is always MYSQL_TYPE_VAR_STRING (i.e., 253).
    The second byte is the field size, i.e., the number of bytes in
    the representation of size of the string: 3 or 4.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_GEOMETRY</td><td>255</td>
    <td>1 byte</td>
    <td>The pack length, i.e., the number of bytes needed to represent
    the length of the geometry: 1, 2, 3, or 4.</td>
  </tr>

  <tr>
    <td>MYSQL_TYPE_TYPED_ARRAY</td><td>15</td>
    <td>up to 4 bytes</td>
    <td>
      - The first byte holds the MySQL type for the elements.
      - The following 0, 1, 2, or 3 bytes holds the metadata for the MySQL
        type for the elements. The contents of these bytes depends on the
        element type, as described in the other rows of this table.
    </td>
  </tr>

  </table>

  The table below lists all optional metadata types, along with the numerical
  identifier for it and the size and interpretation of meta-data used
  to describe the type.

  @anchor Table_table_map_event_optional_metadata
  <table>
  <caption>Table_map_event optional metadata types: numerical identifier and
  metadata. Optional metadata fields are stored in TLV fields.
  Format of values are described in this table. </caption>
  <tr>
    <th>Type</th>
    <th>Description</th>
    <th>Format</th>
  </tr>
  <tr>
    <td>SIGNEDNESS</td>
    <td>signedness of numeric columns. This is included for all values of
    binlog_row_metadata.</td>
    <td>For each numeric column, a bit indicates whether the numeric
    colunm has unsigned flag. 1 means it is unsigned. The number of
    bytes needed for this is int((column_count + 7) / 8). The order is
    the same as the order of column_type field.</td>
  </tr>
  <tr>
    <td>DEFAULT_CHARSET</td>
    <td>Charsets of character columns. It has a default charset for
    the case that most of character columns have same charset and the
    most used charset is binlogged as default charset.Collation
    numbers are binlogged for identifying charsets. They are stored in
    packed length format.  Either DEFAULT_CHARSET or COLUMN_CHARSET is
    included for all values of binlog_row_metadata.</td>
    <td>Default charset's collation is logged first. The charsets which are not
    same to default charset are logged following default charset. They are
    logged as column index and charset collation number pair sequence. The
    column index is counted only in all character columns. The order is same to
    the order of column_type
    field. </td>
  </tr>
  <tr>
    <td>COLUMN_CHARSET</td>
    <td>Charsets of character columns. For the case that most of columns have
    different charsets, this field is logged. It is never logged with
    DEFAULT_CHARSET together.  Either DEFAULT_CHARSET or COLUMN_CHARSET is
    included for all values of binlog_row_metadata.</td>
    <td>It is a collation number sequence for all character columns.</td>
  </tr>
  <tr>
    <td>COLUMN_NAME</td>
    <td>Names of columns. This is only included if
    binlog_row_metadata=FULL.</td>
    <td>A sequence of column names. For each column name, 1 byte for
    the string length in bytes is followed by a string without null
    terminator.</td>
  </tr>
  <tr>
    <td>SET_STR_VALUE</td>
    <td>The string values of SET columns. This is only included if
    binlog_row_metadata=FULL.</td>
    <td>For each SET column, a pack_length representing the value
    count is followed by a sequence of length and string pairs. length
    is the byte count in pack_length format. The string has no null
    terminator.</td>
  </tr>
  <tr>
    <td>ENUM_STR_VALUE</td>
    <td>The string values is ENUM columns. This is only included
    if binlog_row_metadata=FULL.</td>
    <td>The format is the same as SET_STR_VALUE.</td>
  </tr>
  <tr>
    <td>GEOMETRY_TYPE</td>
    <td>The real type of geometry columns. This is only included
    if binlog_row_metadata=FULL.</td>
    <td>A sequence of real type of geometry columns are stored in pack_length
    format. </td>
  </tr>
  <tr>
    <td>SIMPLE_PRIMARY_KEY</td>
    <td>The primary key without any prefix. This is only included
    if binlog_row_metadata=FULL and there is a primary key where every
    key part covers an entire column.</td>
    <td>A sequence of column indexes. The indexes are stored in pack_length
    format.</td>
  </tr>
  <tr>
    <td>PRIMARY_KEY_WITH_PREFIX</td>
    <td>The primary key with some prefix. It doesn't appear together with
    SIMPLE_PRIMARY_KEY. This is only included if
    binlog_row_metadata=FULL and there is a primary key where some key
    part covers a prefix of the column.</td>
    <td>A sequence of column index and prefix length pairs. Both
    column index and prefix length are in pack_length format. Prefix length
    0 means that the whole column value is used.</td>
  </tr>
  <tr>
    <td>ENUM_AND_SET_DEFAULT_CHARSET</td>
    <td>Charsets of ENUM and SET columns. It has the same layout as
    DEFAULT_CHARSET.  If there are SET or ENUM columns and
    binlog_row_metadata=FULL, exactly one of
    ENUM_AND_SET_DEFAULT_CHARSET and ENUM_AND_SET_COLUMN_CHARSET
    appears (the encoder chooses the representation that uses the
    least amount of space).  Otherwise, none of them appears.</td>
    <td>The same format as for DEFAULT_CHARSET, except it counts ENUM
    and SET columns rather than character columns.</td>
  </tr>
  <tr>
    <td>ENUM_AND_SET_COLUMN_CHARSET</td>
    <td>Charsets of ENUM and SET columns. It has the same layout as
    COLUMN_CHARSET.  If there are SET or ENUM columns and
    binlog_row_metadata=FULL, exactly one of
    ENUM_AND_SET_DEFAULT_CHARSET and ENUM_AND_SET_COLUMN_CHARSET
    appears (the encoder chooses the representation that uses the
    least amount of space).  Otherwise, none of them appears.</td>
    <td>The same format as for COLUMN_CHARSET, except it counts ENUM
    and SET columns rather than character columns.</td>
  </tr>
  </table>
*/
class Table_map_event : public Binary_log_event {
 public:
  /** Constants representing offsets */
  enum Table_map_event_offset {
    /** TM = "Table Map" */
    TM_MAPID_OFFSET = 0,
    TM_FLAGS_OFFSET = 6
  };

  typedef uint16_t flag_set;

  /**
    DEFAULT_CHARSET and COLUMN_CHARSET don't appear together, and
    ENUM_AND_SET_DEFAULT_CHARSET and ENUM_AND_SET_COLUMN_CHARSET don't
    appear together. They are just alternative ways to pack character
    set information. When binlogging, it logs character sets in the
    way that occupies least storage.

    SIMPLE_PRIMARY_KEY and PRIMARY_KEY_WITH_PREFIX don't appear together.
    SIMPLE_PRIMARY_KEY is for the primary keys which only use whole values of
    pk columns. PRIMARY_KEY_WITH_PREFIX is
    for the primary keys which just use part value of pk columns.
   */
  enum Optional_metadata_field_type {
    SIGNEDNESS = 1,  // UNSIGNED flag of numeric columns
    DEFAULT_CHARSET, /* Character set of string columns, optimized to
                        minimize space when many columns have the
                        same charset. */
    COLUMN_CHARSET,  /* Character set of string columns, optimized to
                        minimize space when columns have many
                        different charsets. */
    COLUMN_NAME,
    SET_STR_VALUE,                // String value of SET columns
    ENUM_STR_VALUE,               // String value of ENUM columns
    GEOMETRY_TYPE,                // Real type of geometry columns
    SIMPLE_PRIMARY_KEY,           // Primary key without prefix
    PRIMARY_KEY_WITH_PREFIX,      // Primary key with prefix
    ENUM_AND_SET_DEFAULT_CHARSET, /* Character set of enum and set
                                     columns, optimized to minimize
                                     space when many columns have the
                                     same charset. */
    ENUM_AND_SET_COLUMN_CHARSET,  /* Character set of enum and set
                                     columns, optimized to minimize
                                     space when many columns have the
                                     same charset. */
    COLUMN_VISIBILITY             /* Flag to indicate column visibility
                                     attribute. */
  };

  /**
    Metadata_fields organizes m_optional_metadata into a structured format which
    is easy to access.
  */
  struct Optional_metadata_fields {
    typedef std::pair<unsigned int, unsigned int> uint_pair;
    typedef std::vector<std::string> str_vector;

    struct Default_charset {
      Default_charset() : default_charset(0) {}
      bool empty() const { return default_charset == 0; }

      // Default charset for the columns which are not in charset_pairs.
      unsigned int default_charset;

      /* The uint_pair means <column index, column charset number>. */
      std::vector<uint_pair> charset_pairs;
    };

    // Contents of DEFAULT_CHARSET field are converted into Default_charset.
    Default_charset m_default_charset;
    // Contents of ENUM_AND_SET_DEFAULT_CHARSET are converted into
    // Default_charset.
    Default_charset m_enum_and_set_default_charset;
    std::vector<bool> m_signedness;
    // Character set number of every string column
    std::vector<unsigned int> m_column_charset;
    // Character set number of every ENUM or SET column.
    std::vector<unsigned int> m_enum_and_set_column_charset;
    std::vector<std::string> m_column_name;
    // each str_vector stores values of one enum/set column
    std::vector<str_vector> m_enum_str_value;
    std::vector<str_vector> m_set_str_value;
    std::vector<unsigned int> m_geometry_type;
    /*
      The uint_pair means <column index, prefix length>.  Prefix length is 0 if
      whole column value is used.
    */
    std::vector<uint_pair> m_primary_key;
    std::vector<bool> m_column_visibility;

    /*
      It parses m_optional_metadata and populates into above variables.

      @param[in] optional_metadata points to the begin of optional metadata
                                   fields in table_map_event.
      @param[in] optional_metadata_len length of optional_metadata field.
     */
    Optional_metadata_fields(unsigned char *optional_metadata,
                             unsigned int optional_metadata_len);
    // It is used to specify the validity of the deserialized structure
    bool is_valid;
  };

  /**
    <pre>
    The buffer layout for fixed data part is as follows:
    +-----------------------------------+
    | table_id | Reserved for future use|
    +-----------------------------------+
    </pre>

    <pre>
    The buffer layout for variable data part is as follows:
    +--------------------------------------------------------------------------+
    | db len| db name | table len| table name | no of cols | array of col types|
    +--------------------------------------------------------------------------+
    +---------------------------------------------+
    | metadata len | metadata block | m_null_bits |
    +---------------------------------------------+
    </pre>

    @param buf  Contains the serialized event.
    @param fde  An FDE event (see Rotate_event constructor for more info).
  */
  Table_map_event(const char *buf, const Format_description_event *fde);

  Table_map_event(const Table_id &tid, unsigned long colcnt, const char *dbnam,
                  size_t dblen, const char *tblnam, size_t tbllen)
      : Binary_log_event(TABLE_MAP_EVENT),
        m_table_id(tid),
        m_data_size(0),
        m_dbnam(""),
        m_dblen(dblen),
        m_tblnam(""),
        m_tbllen(tbllen),
        m_colcnt(colcnt),
        m_field_metadata_size(0),
        m_field_metadata(nullptr),
        m_null_bits(nullptr),
        m_optional_metadata_len(0),
        m_optional_metadata(nullptr) {
    if (dbnam) m_dbnam = std::string(dbnam, m_dblen);
    if (tblnam) m_tblnam = std::string(tblnam, m_tbllen);
  }

  ~Table_map_event() override;

  /** Event post header contents */
  Table_id m_table_id;
  flag_set m_flags;

  size_t m_data_size; /** event data size */

  /** Event body contents */
  std::string m_dbnam;
  unsigned long long int m_dblen;
  std::string m_tblnam;
  unsigned long long int m_tbllen;
  unsigned long m_colcnt;
  unsigned char *m_coltype;

  /**
    The size of field metadata buffer set by calling save_field_metadata()
  */
  unsigned long m_field_metadata_size;
  unsigned char *m_field_metadata; /** field metadata */
  unsigned char *m_null_bits;
  unsigned int m_optional_metadata_len;
  unsigned char *m_optional_metadata;

  Table_map_event()
      : Binary_log_event(TABLE_MAP_EVENT),
        m_coltype(nullptr),
        m_field_metadata_size(0),
        m_field_metadata(nullptr),
        m_null_bits(nullptr),
        m_optional_metadata_len(0),
        m_optional_metadata(nullptr) {}

  unsigned long long get_table_id() { return m_table_id.id(); }
  std::string get_table_name() { return m_tblnam; }
  std::string get_db_name() { return m_dbnam; }

#ifndef HAVE_MYSYS
  void print_event_info(std::ostream &info) override;
  void print_long_info(std::ostream &info) override;
#endif
};

/**
  @class Rows_event

 Common base class for all row-containing binary log events.

 RESPONSIBILITIES

   - Provide an interface for adding an individual row to the event.

  @section Rows_event_binary_format Binary Format

  The Post-Header has the following components:

  <table>
  <caption>Post-Header for Rows_event</caption>

  <tr>
    <th>Name</th>
    <th>Format</th>
    <th>Description</th>
  </tr>

  <tr>
    <td>table_id</td>
    <td>6 bytes unsigned integer</td>
    <td>The number that identifies the table</td>
  </tr>

  <tr>
    <td>flags</td>
    <td>2 byte bitfield</td>
    <td>Reserved for future use; currently always 0.</td>
  </tr>

  </table>

  The Body has the following components:

  <table>
  <caption>Body for Rows_event</caption>

  <tr>
    <th>Name</th>
    <th>Format</th>
    <th>Description</th>
  </tr>


  <tr>
    <td>width</td>
    <td>packed integer</td>
    <td>Represents the number of columns in the table</td>
  </tr>

  <tr>
    <td>cols</td>
    <td>Bitfield, variable sized</td>
    <td>Indicates whether each column is used, one bit per column.
        For this field, the amount of storage required is
        INT((width + 7) / 8) bytes. </td>
  </tr>

  <tr>
    <td>extra_row_info</td>
    <td>An object of class Extra_row_info</td>
    <td>The class Extra_row_info will be storing the information related
        to m_extra_row_ndb_info and partition info (partition_id and
        source_partition_id). At any given time a Rows_event can have both, one
        or none of ndb_info and partition_info present as part of Rows_event.
        In case both ndb_info and partition_info are present then below will
        be the order in which they will be stored.

        @verbatim
        +----------+--------------------------------------+
        |type_code |        extra_row_ndb_info            |
        +--- ------+--------------------------------------+
        | NDB      |Len of ndb_info |Format |ndb_data     |
        | 1 byte   |1 byte          |1 byte |len - 2 byte |
        +----------+----------------+-------+-------------+

        In case of INSERT/DELETE
        +-----------+----------------+
        | type_code | partition_info |
        +-----------+----------------+
        |   PART    |  partition_id  |
        | (1 byte)  |     2 byte     |
        +-----------+----------------+

        In case of UPDATE
        +-----------+------------------------------------+
        | type_code |        partition_info              |
        +-----------+--------------+---------------------+
        |   PART    | partition_id | source_partition_id |
        | (1 byte)  |    2 byte    |       2 byte        |
        +-----------+--------------+---------------------+

        source_partition_id is used only in the case of Update_event
        to log the partition_id of the source partition.

        @endverbatim
        This is the format for any information stored as extra_row_info.
        type_code is not a part of the class Extra_row_info as it is a constant
        values used at the time of serializing and decoding the event.
   </td>
  </tr>

  <tr>
    <td>columns_before_image</td>
    <td>vector of elements of type uint8_t</td>
    <td>For DELETE and UPDATE only.
        Bit-field indicating whether each column is used
        one bit per column. For this field, the amount of storage
        required for N columns is INT((N + 7) / 8) bytes.</td>
  </tr>

  <tr>
    <td>columns_after_image</td>
    <td>vector of elements of type uint8_t</td>
    <td>For WRITE and UPDATE only.
        Bit-field indicating whether each column is used in the
        UPDATE_ROWS_EVENT and WRITE_ROWS_EVENT after-image; one bit per column.
        For this field, the amount of storage required for N columns
        is INT((N + 7) / 8) bytes.

        @verbatim
          +-------------------------------------------------------+
          | Event Type | Cols_before_image | Cols_after_image     |
          +-------------------------------------------------------+
          |  DELETE    |   Deleted row     |    NULL              |
          |  INSERT    |   NULL            |    Inserted row      |
          |  UPDATE    |   Old     row     |    Updated row       |
          +-------------------------------------------------------+
        @endverbatim
    </td>
  </tr>

  <tr>
    <td>row</td>
    <td>vector of elements of type uint8_t</td>
    <td> A sequence of zero or more rows. The end is determined by the size
         of the event. Each row has the following format:
           - A Bit-field indicating whether each field in the row is NULL.
             Only columns that are "used" according to the second field in
             the variable data part are listed here. If the second field in
             the variable data part has N one-bits, the amount of storage
             required for this field is INT((N + 7) / 8) bytes.
           - The row-image, containing values of all table fields. This only
             lists table fields that are used (according to the second field
             of the variable data part) and non-NULL (according to the
             previous field). In other words, the number of values listed here
             is equal to the number of zero bits in the previous field.
             (not counting padding bits in the last byte).
             @verbatim
                For example, if a INSERT statement inserts into 4 columns of a
                table, N= 4 (in the formula above).
                length of bitmask= (4 + 7) / 8 = 1
                Number of fields in the row= 4.

                        +------------------------------------------------+
                        |Null_bit_mask(4)|field-1|field-2|field-3|field 4|
                        +------------------------------------------------+
             @endverbatim
    </td>
  </tr>
  </table>
*/
class Rows_event : public Binary_log_event {
 public:
  /**
    These definitions allow to combine the flags into an
    appropriate flag set using the normal bitwise operators.  The
    implicit conversion from an enum-constant to an integer is
    accepted by the compiler, which is then used to set the real set
    of flags.
  */
  enum enum_flag {
    /** Last event of a statement */
    STMT_END_F = (1U << 0),
    /** Value of the OPTION_NO_FOREIGN_KEY_CHECKS flag in thd->options */
    NO_FOREIGN_KEY_CHECKS_F = (1U << 1),
    /** Value of the OPTION_RELAXED_UNIQUE_CHECKS flag in thd->options */
    RELAXED_UNIQUE_CHECKS_F = (1U << 2),
    /**
      Indicates that rows in this event are complete, that is contain
      values for all columns of the table.
    */
    COMPLETE_ROWS_F = (1U << 3),
    /**
      Flags for everything. Please update when you add new flags.
     */
    ALL_FLAGS = STMT_END_F | NO_FOREIGN_KEY_CHECKS_F | RELAXED_UNIQUE_CHECKS_F |
                COMPLETE_ROWS_F
  };

  /**
    Constructs an event directly. The members are assigned default values.

    @param type_arg          Type of ROW_EVENT. Expected types are:
                             - WRITE_ROWS_EVENT
                             - UPDATE_ROWS_EVENT, PARTIAL_UPDATE_ROWS_EVENT
                             - DELETE_ROWS_EVENT
  */
  explicit Rows_event(Log_event_type type_arg)
      : Binary_log_event(type_arg),
        m_table_id(0),
        m_width(0),
        columns_before_image(0),
        columns_after_image(0),
        row(0) {}
  /**
    The constructor is responsible for decoding the event contained in
    the buffer.

    <pre>
    The buffer layout for fixed data part is as follows
    +------------------------------------+
    | table_id | reserved for future use |
    +------------------------------------+
    </pre>

    <pre>
    The buffer layout for variable data part is as follows
    +------------------------------------------------------------------+
    | var_header_len | column_before_image | columns_after_image | row |
    +------------------------------------------------------------------+
    </pre>

    @param buf  Contains the serialized event.
    @param fde  An FDE event (see Rotate_event constructor for more info).
  */
  Rows_event(const char *buf, const Format_description_event *fde);

  ~Rows_event() override;

 protected:
  Log_event_type m_type; /** Actual event type */

  /** Post header content */
  Table_id m_table_id;
  uint16_t m_flags; /** Flags for row-level events */

  /* Body of the event */
  unsigned long m_width; /** The width of the columns bitmap */
  uint32_t n_bits_len;   /** value determined by (m_width + 7) / 8 */
  uint16_t var_header_len;

  std::vector<uint8_t> columns_before_image;
  std::vector<uint8_t> columns_after_image;
  std::vector<uint8_t> row;

 public:
  class Extra_row_info {
   private:
    /** partition_id for a row in a partitioned table */
    int m_partition_id;
    /**
      It is the partition_id of the source partition in case
      of Update_event, the target's partition_id is m_partition_id.
      This variable is used only in case of Update_event.
    */
    int m_source_partition_id;
    /** The extra row info provided by NDB */
    unsigned char *m_extra_row_ndb_info;

   public:
    Extra_row_info()
        : m_partition_id(UNDEFINED),
          m_source_partition_id(UNDEFINED),
          m_extra_row_ndb_info(nullptr) {}

    Extra_row_info(const Extra_row_info &) = delete;

    int get_partition_id() const { return m_partition_id; }
    void set_partition_id(int partition_id) {
      BAPI_ASSERT(partition_id <= 65535);
      m_partition_id = partition_id;
    }

    int get_source_partition_id() const { return m_source_partition_id; }
    void set_source_partition_id(int source_partition_id) {
      BAPI_ASSERT(source_partition_id <= 65535);
      m_source_partition_id = source_partition_id;
    }

    unsigned char *get_ndb_info() const { return m_extra_row_ndb_info; }
    void set_ndb_info(const unsigned char *ndb_info, size_t len) {
      BAPI_ASSERT(!have_ndb_info());
      m_extra_row_ndb_info =
          static_cast<unsigned char *>(bapi_malloc(len, 16 /* flags */));
      std::copy(ndb_info, ndb_info + len, m_extra_row_ndb_info);
    }
    /**
      Compares the extra_row_info in a Row event, it checks three things
      1. The m_extra_row_ndb_info pointers. It compares their significant bytes.
      2. Partition_id
      3. source_partition_id

      @return
       true   all the above variables are same in the event and the one passed
              in parameter.
       false  Any of the above variable has a different value.
    */
    bool compare_extra_row_info(const unsigned char *ndb_info_arg,
                                int part_id_arg, int source_part_id);

    bool have_part() const { return m_partition_id != UNDEFINED; }

    bool have_ndb_info() const { return m_extra_row_ndb_info != nullptr; }
    size_t get_ndb_length();
    size_t get_part_length();
    ~Extra_row_info();

    static const int UNDEFINED{INT_MAX};
  };
  Extra_row_info m_extra_row_info;

  unsigned long long get_table_id() const { return m_table_id.id(); }

  enum_flag get_flags() const { return static_cast<enum_flag>(m_flags); }

  uint32_t get_null_bits_len() const { return n_bits_len; }

  unsigned long get_width() const { return m_width; }

  /**
    @brief Gets the flags listed as strings. If there are no flags set, returns
    an empty string.

    @return A string with the names of the flags set. If no flag is set, returns
    an empty string.
   */
  std::string get_enum_flag_string() const {
    assert((STMT_END_F | NO_FOREIGN_KEY_CHECKS_F | RELAXED_UNIQUE_CHECKS_F |
            COMPLETE_ROWS_F) == ALL_FLAGS);
    if (!m_flags) return "";
    std::stringstream ss;
    ss << " flags:";
    if (m_flags & STMT_END_F) ss << " STMT_END_F";
    if (m_flags & NO_FOREIGN_KEY_CHECKS_F) ss << " NO_FOREIGN_KEY_CHECKS_F";
    if (m_flags & RELAXED_UNIQUE_CHECKS_F) ss << " RELAXED_UNIQUE_CHECKS_F";
    if (m_flags & COMPLETE_ROWS_F) ss << " COMPLETE_ROWS_F";
    if (m_flags & ~ALL_FLAGS) {
      assert(false);
      auto unknown_flags = m_flags & ~ALL_FLAGS;
      ss << " UNKNOWN_FLAG(" << std::hex << "0x" << unknown_flags << ")";
    }
    return ss.str();
  }

  /**
    Gets a string describing the flags.

    @param flag A set of flags to get the description for.
    @return a string describing the flags.
  */
  static std::string get_flag_string(enum_flag flag) {
    assert((STMT_END_F | NO_FOREIGN_KEY_CHECKS_F | RELAXED_UNIQUE_CHECKS_F |
            COMPLETE_ROWS_F) == ALL_FLAGS);
    std::string str = "";
    if (flag & STMT_END_F) str.append(" Last event of the statement");
    if (flag & NO_FOREIGN_KEY_CHECKS_F) str.append(" No foreign Key checks");
    if (flag & RELAXED_UNIQUE_CHECKS_F) str.append(" No unique key checks");
    if (flag & COMPLETE_ROWS_F) str.append(" Complete Rows");
    if (flag & ~ALL_FLAGS) str.append(" Unknown Flag");
    return str;
  }
#ifndef HAVE_MYSYS
  void print_event_info(std::ostream &info) override;
  void print_long_info(std::ostream &info) override;
#endif

  template <class Iterator_value_type>
  friend class Row_event_iterator;
};

/**
  @class Write_rows_event

  Log row insertions. The event contain several  insert/update rows
  for a table. Note that each event contains only  rows for one table.

  @section Write_rows_event_binary_format Binary Format
*/
class Write_rows_event : public virtual Rows_event {
 public:
  Write_rows_event(const char *buf, const Format_description_event *fde);
  Write_rows_event() : Rows_event(WRITE_ROWS_EVENT) {}
};

/**
  @class Update_rows_event

  Log row updates with a before image. The event contain several
  update rows for a table. Note that each event contains only rows for
  one table.

  Also note that the row data consists of pairs of row data: one row
  for the old data and one row for the new data.

  @section Update_rows_event_binary_format Binary Format
*/
class Update_rows_event : public virtual Rows_event {
 public:
  Update_rows_event(const char *buf, const Format_description_event *fde);
  Update_rows_event(Log_event_type event_type) : Rows_event(event_type) {}
};

/**
  @class Delete_rows_event

  Log row deletions. The event contain several delete rows for a
  table. Note that each event contains only rows for one table.

  RESPONSIBILITIES

    - Act as a container for rows that has been deleted on the master
      and should be deleted on the slave.

   @section Delete_rows_event_binary_format Binary Format
*/
class Delete_rows_event : public virtual Rows_event {
 public:
  Delete_rows_event(const char *buf, const Format_description_event *fde);
  Delete_rows_event() : Rows_event(DELETE_ROWS_EVENT) {}
};

/**
  @class Rows_query_event

  Rows query event type, which is a subclass
  of the Ignorable_event, to record the original query for the rows
  events in RBR. This event can be used to display the original query as
  comments by SHOW BINLOG EVENTS query, or mysqlbinlog client when the
  --verbose option is given twice

  @section Rows_query_var_event_binary_format Binary Format

  The Post-Header for this event type is empty. The Body has one
  component:

  <table>
  <caption>Body for Rows_query_event</caption>

  <tr>
    <th>Name</th>
    <th>Format</th>
    <th>Description</th>
  </tr>

  <tr>
    <td>m_rows_query</td>
    <td>char array</td>
    <td>Records the original query executed in RBR </td>
  </tr>
  </table>
*/
class Rows_query_event : public virtual Ignorable_event {
 public:
  /**
    It is used to write the original query in the binlog file in case of RBR
    when the session flag binlog_rows_query_log_events is set.

    <pre>
    The buffer layout is as follows:
    +------------------------------------+
    | The original query executed in RBR |
    +------------------------------------+
    </pre>

    @param buf  Contains the serialized event.
    @param fde  An FDE event (see Rotate_event constructor for more info).
  */

  Rows_query_event(const char *buf, const Format_description_event *fde);
  /**
    It is the minimal constructor, and all it will do is set the type_code as
    ROWS_QUERY_LOG_EVENT in the header object in Binary_log_event.
  */
  Rows_query_event()
      : Ignorable_event(ROWS_QUERY_LOG_EVENT), m_rows_query(nullptr) {}

  ~Rows_query_event() override;

 protected:
  char *m_rows_query;
};
}  // namespace mysql::binlog::event

/// @}

#endif  // MYSQL_BINLOG_EVENT_ROWS_EVENT_H
