package com.taobao.tddl.dbsync.binlog.event;
import java.util.BitSet;
import com.taobao.tddl.dbsync.binlog.LogBuffer;
import com.taobao.tddl.dbsync.binlog.LogEvent;
/**
* In row-based mode, every row operation event is preceded by a
* Table_map_log_event which maps a table definition to a number. The table
* definition consists of database name, table name, and column definitions. The
* Post-Header has the following components:
* <table>
* <caption>Post-Header for Table_map_log_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_log_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 one byte unsigned 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>one byte string length, 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>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 Table_table_map_log_event_column_types "below" (along
* with description of the associated metadata field).</td>
* </tr>
* <tr>
* <td>metadata_length</td>
* <td>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
* Table_table_map_log_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>
* </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.
* <table>
* <caption>Table_map_log_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>–</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>–</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>–</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>–</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>–</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>–</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>
* </table>
*
* @author <a href="mailto:changyuan.lh@taobao.com">Changyuan.lh</a>
* @version 1.0
*/
public final class TableMapLogEvent extends LogEvent {
/**
* Fixed data part:
* <ul>
* <li>6 bytes. The table ID.</li>
* <li>2 bytes. Reserved for future use.</li>
* </ul>
* <p>
* Variable data part:
* <ul>
* <li>1 byte. The length of the database name.</li>
* <li>Variable-sized. The database name (null-terminated).</li>
* <li>1 byte. The length of the table name.</li>
* <li>Variable-sized. The table name (null-terminated).</li>
* <li>Packed integer. The number of columns in the table.</li>
* <li>Variable-sized. An array of column types, one byte per column.</li>
* <li>Packed integer. The length of the metadata block.</li>
* <li>Variable-sized. The metadata block; see log_event.h for contents and
* format.</li>
* <li>Variable-sized. Bit-field indicating whether each column can be NULL,
* one bit per column. For this field, the amount of storage required for N
* columns is INT((N+7)/8) bytes.</li>
* </ul>
* Source : http://forge.mysql.com/wiki/MySQL_Internals_Binary_Log
*/
protected final String dbname;
protected final String tblname;
/**
* Holding mysql column information.
*/
public static final class ColumnInfo {
public int type;
public int meta;
}
protected final int columnCnt;
protected final ColumnInfo[] columnInfo; // buffer for field
// metadata
protected final long tableId;
protected BitSet nullBits;
/** TM = "Table Map" */
public static final int TM_MAPID_OFFSET = 0;
public static final int TM_FLAGS_OFFSET = 6;
/**
* Constructor used by slave to read the event from the binary log.
*/
public TableMapLogEvent(LogHeader header, LogBuffer buffer, FormatDescriptionLogEvent descriptionEvent){
super(header);
final int commonHeaderLen = descriptionEvent.commonHeaderLen;
final int postHeaderLen = descriptionEvent.postHeaderLen[header.type - 1];
/* Read the post-header */
buffer.position(commonHeaderLen + TM_MAPID_OFFSET);
if (postHeaderLen == 6) {
/*
* Master is of an intermediate source tree before 5.1.4. Id is 4
* bytes
*/
tableId = buffer.getUint32();
} else {
// DBUG_ASSERT(post_header_len == TABLE_MAP_HEADER_LEN);
tableId = buffer.getUlong48();
}
// flags = buffer.getUint16();
/* Read the variable part of the event */
buffer.position(commonHeaderLen + postHeaderLen);
dbname = buffer.getString();
buffer.forward(1); /* termination null */
tblname = buffer.getString();
buffer.forward(1); /* termination null */
// Read column information from buffer
columnCnt = (int) buffer.getPackedLong();
columnInfo = new ColumnInfo[columnCnt];
for (int i = 0; i < columnCnt; i++) {
ColumnInfo info = new ColumnInfo();
info.type = buffer.getUint8();
columnInfo[i] = info;
}
if (buffer.position() < buffer.limit()) {
final int fieldSize = (int) buffer.getPackedLong();
decodeFields(buffer, fieldSize);
nullBits = buffer.getBitmap(columnCnt);
}
}
/**
* Decode field metadata by column types.
*
* @see mysql-5.1.60/sql/rpl_utility.h
*/
private final void decodeFields(LogBuffer buffer, final int len) {
final int limit = buffer.limit();
buffer.limit(len + buffer.position());
for (int i = 0; i < columnCnt; i++) {
ColumnInfo info = columnInfo[i];
switch (info.type) {
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_DOUBLE:
case MYSQL_TYPE_FLOAT:
case MYSQL_TYPE_GEOMETRY:
case MYSQL_TYPE_JSON:
/*
* These types store a single byte.
*/
info.meta = buffer.getUint8();
break;
case MYSQL_TYPE_SET:
case MYSQL_TYPE_ENUM:
/*
* log_event.h : MYSQL_TYPE_SET & MYSQL_TYPE_ENUM : This
* enumeration value is only used internally and cannot
* exist in a binlog.
*/
logger.warn("This enumeration value is only used internally "
+ "and cannot exist in a binlog: type=" + info.type);
break;
case MYSQL_TYPE_STRING: {
/*
* log_event.h : 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.
*/
int x = (buffer.getUint8() << 8); // real_type
x += buffer.getUint8(); // pack or field length
info.meta = x;
break;
}
case MYSQL_TYPE_BIT:
info.meta = buffer.getUint16();
break;
case MYSQL_TYPE_VARCHAR:
/*
* These types store two bytes.
*/
info.meta = buffer.getUint16();
break;
case MYSQL_TYPE_NEWDECIMAL: {
int x = buffer.getUint8() << 8; // precision
x += buffer.getUint8(); // decimals
info.meta = x;
break;
}
case MYSQL_TYPE_TIME2:
case MYSQL_TYPE_DATETIME2:
case MYSQL_TYPE_TIMESTAMP2: {
info.meta = buffer.getUint8();
break;
}
default:
info.meta = 0;
break;
}
}
buffer.limit(limit);
}
public final String getDbName() {
return dbname;
}
public final String getTableName() {
return tblname;
}
public final int getColumnCnt() {
return columnCnt;
}
public final ColumnInfo[] getColumnInfo() {
return columnInfo;
}
public final long getTableId() {
return tableId;
}
}