Search in sources :

Example 1 with QueryEventData

use of com.github.shyiko.mysql.binlog.event.QueryEventData in project debezium by debezium.

the class BinlogReader method handleQueryEvent.

/**
 * Handle the supplied event with an {@link QueryEventData} by possibly recording the DDL statements as changes in the
 * MySQL schemas.
 *
 * @param event the database change data event to be processed; may not be null
 * @throws InterruptedException if this thread is interrupted while recording the DDL statements
 */
protected void handleQueryEvent(Event event) throws InterruptedException {
    QueryEventData command = unwrapData(event);
    logger.debug("Received query command: {}", event);
    String sql = command.getSql().trim();
    if (sql.equalsIgnoreCase("BEGIN")) {
        // We are starting a new transaction ...
        source.startNextTransaction();
        source.setBinlogThread(command.getThreadId());
        if (initialEventsToSkip != 0) {
            logger.debug("Restarting partially-processed transaction; change events will not be created for the first {} events plus {} more rows in the next event", initialEventsToSkip, startingRowNumber);
            // We are restarting, so we need to skip the events in this transaction that we processed previously...
            skipEvent = true;
        }
        return;
    }
    if (sql.equalsIgnoreCase("COMMIT")) {
        handleTransactionCompletion(event);
        return;
    }
    if (sql.toUpperCase().startsWith("XA ")) {
        // This is an XA transaction, and we currently ignore these and do nothing ...
        return;
    }
    if (context.ddlFilter().test(sql)) {
        logger.debug("DDL '{}' was filtered out of processing", sql);
        return;
    }
    if (sql.equalsIgnoreCase("ROLLBACK")) {
        // We have hit a ROLLBACK which is not supported
        logger.warn("Rollback statements cannot be handled without binlog buffering, the connector will fail. Please check '{}' to see how to enable buffering", MySqlConnectorConfig.BUFFER_SIZE_FOR_BINLOG_READER.name());
    }
    context.dbSchema().applyDdl(context.source(), command.getDatabase(), command.getSql(), (dbName, statements) -> {
        if (recordSchemaChangesInSourceRecords && recordMakers.schemaChanges(dbName, statements, super::enqueueRecord) > 0) {
            logger.debug("Recorded DDL statements for database '{}': {}", dbName, statements);
        }
    });
}
Also used : QueryEventData(com.github.shyiko.mysql.binlog.event.QueryEventData)

Example 2 with QueryEventData

use of com.github.shyiko.mysql.binlog.event.QueryEventData in project debezium by debezium.

the class EventBuffer method add.

/**
 * An entry point to the buffer that should be used by BinlogReader to push events.
 * @param event to be stored in the buffer
 */
public void add(Event event) {
    if (event == null) {
        return;
    }
    // besides directly emitting the events
    if (isReplayingEventsBeyondBufferCapacity()) {
        reader.handleEvent(event);
        return;
    }
    if (event.getHeader().getEventType() == EventType.QUERY) {
        QueryEventData command = reader.unwrapData(event);
        LOGGER.debug("Received query command: {}", event);
        String sql = command.getSql().trim();
        if (sql.equalsIgnoreCase("BEGIN")) {
            beginTransaction(event);
        } else if (sql.equalsIgnoreCase("COMMIT")) {
            completeTransaction(true, event);
        } else if (sql.equalsIgnoreCase("ROLLBACK")) {
            rollbackTransaction();
        } else {
            consumeEvent(event);
        }
    } else if (event.getHeader().getEventType() == EventType.XID) {
        completeTransaction(true, event);
    } else {
        consumeEvent(event);
    }
}
Also used : QueryEventData(com.github.shyiko.mysql.binlog.event.QueryEventData)

Example 3 with QueryEventData

use of com.github.shyiko.mysql.binlog.event.QueryEventData in project SpinalTap by airbnb.

the class MysqlSource method toBinlogEvent.

public static BinlogEvent toBinlogEvent(Event event, BinlogFilePos filePos) {
    EventHeaderV4 header = event.getHeader();
    EventType eventType = header.getEventType();
    long serverId = header.getServerId();
    long timestamp = header.getTimestamp();
    if (EventType.isWrite(eventType)) {
        WriteRowsEventData data = event.getData();
        return new WriteEvent(data.getTableId(), serverId, timestamp, filePos, data.getRows());
    } else if (EventType.isUpdate(eventType)) {
        UpdateRowsEventData data = event.getData();
        return new UpdateEvent(data.getTableId(), serverId, timestamp, filePos, data.getRows());
    } else if (EventType.isDelete(eventType)) {
        DeleteRowsEventData data = event.getData();
        return new DeleteEvent(data.getTableId(), serverId, timestamp, filePos, data.getRows());
    } else {
        switch(eventType) {
            case TABLE_MAP:
                TableMapEventData tableMapData = event.getData();
                return new TableMapEvent(tableMapData.getTableId(), serverId, timestamp, filePos, tableMapData.getDatabase(), tableMapData.getTable(), tableMapData.getColumnTypes());
            case XID:
                XidEventData xidData = event.getData();
                return new XidEvent(serverId, timestamp, filePos, xidData.getXid());
            case QUERY:
                QueryEventData queryData = event.getData();
                return new QueryEvent(serverId, timestamp, filePos, queryData.getDatabase(), queryData.getSql());
            case FORMAT_DESCRIPTION:
                return new StartEvent(serverId, timestamp, filePos);
            default:
                return null;
        }
    }
}
Also used : WriteEvent(com.airbnb.spinaltap.mysql.event.WriteEvent) QueryEventData(com.github.shyiko.mysql.binlog.event.QueryEventData) TableMapEvent(com.airbnb.spinaltap.mysql.event.TableMapEvent) WriteRowsEventData(com.github.shyiko.mysql.binlog.event.WriteRowsEventData) EventType(com.github.shyiko.mysql.binlog.event.EventType) UpdateRowsEventData(com.github.shyiko.mysql.binlog.event.UpdateRowsEventData) DeleteRowsEventData(com.github.shyiko.mysql.binlog.event.DeleteRowsEventData) XidEventData(com.github.shyiko.mysql.binlog.event.XidEventData) QueryEvent(com.airbnb.spinaltap.mysql.event.QueryEvent) DeleteEvent(com.airbnb.spinaltap.mysql.event.DeleteEvent) TableMapEventData(com.github.shyiko.mysql.binlog.event.TableMapEventData) StartEvent(com.airbnb.spinaltap.mysql.event.StartEvent) EventHeaderV4(com.github.shyiko.mysql.binlog.event.EventHeaderV4) XidEvent(com.airbnb.spinaltap.mysql.event.XidEvent) UpdateEvent(com.airbnb.spinaltap.mysql.event.UpdateEvent)

Example 4 with QueryEventData

use of com.github.shyiko.mysql.binlog.event.QueryEventData in project rocketmq-externals by apache.

the class EventProcessor method processQueryEvent.

private void processQueryEvent(Event event) {
    QueryEventData data = event.getData();
    String sql = data.getSql();
    if (createTablePattern.matcher(sql).find()) {
        schema.reset();
    }
}
Also used : QueryEventData(com.github.shyiko.mysql.binlog.event.QueryEventData)

Example 5 with QueryEventData

use of com.github.shyiko.mysql.binlog.event.QueryEventData in project nifi by apache.

the class CaptureChangeMySQL method outputEvents.

public void outputEvents(ProcessSession session, StateManager stateManager, ComponentLog log) throws IOException {
    RawBinlogEvent rawBinlogEvent;
    // Drain the queue
    while ((rawBinlogEvent = queue.poll()) != null && !doStop.get()) {
        Event event = rawBinlogEvent.getEvent();
        EventHeaderV4 header = event.getHeader();
        long timestamp = header.getTimestamp();
        EventType eventType = header.getEventType();
        // is filled in during the ROTATE case
        if (eventType != ROTATE && eventType != FORMAT_DESCRIPTION) {
            currentBinlogPosition = header.getPosition();
        }
        log.debug("Got message event type: {} ", new Object[] { header.getEventType().toString() });
        switch(eventType) {
            case TABLE_MAP:
                // This is sent to inform which table is about to be changed by subsequent events
                TableMapEventData data = event.getData();
                // Should we skip this table? Yes if we've specified a DB or table name pattern and they don't match
                skipTable = (databaseNamePattern != null && !databaseNamePattern.matcher(data.getDatabase()).matches()) || (tableNamePattern != null && !tableNamePattern.matcher(data.getTable()).matches());
                if (!skipTable) {
                    TableInfoCacheKey key = new TableInfoCacheKey(this.getIdentifier(), data.getDatabase(), data.getTable(), data.getTableId());
                    if (cacheClient != null) {
                        try {
                            currentTable = cacheClient.get(key, cacheKeySerializer, cacheValueDeserializer);
                        } catch (ConnectException ce) {
                            throw new IOException("Could not connect to Distributed Map Cache server to get table information", ce);
                        }
                        if (currentTable == null) {
                            // We don't have an entry for this table yet, so fetch the info from the database and populate the cache
                            try {
                                currentTable = loadTableInfo(key);
                                try {
                                    cacheClient.put(key, currentTable, cacheKeySerializer, cacheValueSerializer);
                                } catch (ConnectException ce) {
                                    throw new IOException("Could not connect to Distributed Map Cache server to put table information", ce);
                                }
                            } catch (SQLException se) {
                                // Propagate the error up, so things like rollback and logging/bulletins can be handled
                                throw new IOException(se.getMessage(), se);
                            }
                        }
                    }
                } else {
                    // Clear the current table, to force a reload next time we get a TABLE_MAP event we care about
                    currentTable = null;
                }
                break;
            case QUERY:
                QueryEventData queryEventData = event.getData();
                currentDatabase = queryEventData.getDatabase();
                String sql = queryEventData.getSql();
                // Is this the start of a transaction?
                if ("BEGIN".equals(sql)) {
                    // If we're already in a transaction, something bad happened, alert the user
                    if (inTransaction) {
                        throw new IOException("BEGIN event received while already processing a transaction. This could indicate that your binlog position is invalid.");
                    }
                    // Mark the current binlog position in case we have to rollback the transaction (if the processor is stopped, e.g.)
                    xactBinlogFile = currentBinlogFile;
                    xactBinlogPosition = currentBinlogPosition;
                    xactSequenceId = currentSequenceId.get();
                    if (includeBeginCommit && (databaseNamePattern == null || databaseNamePattern.matcher(currentDatabase).matches())) {
                        BeginTransactionEventInfo beginEvent = new BeginTransactionEventInfo(currentDatabase, timestamp, currentBinlogFile, currentBinlogPosition);
                        currentSequenceId.set(beginEventWriter.writeEvent(currentSession, transitUri, beginEvent, currentSequenceId.get(), REL_SUCCESS));
                    }
                    inTransaction = true;
                } else if ("COMMIT".equals(sql)) {
                    if (!inTransaction) {
                        throw new IOException("COMMIT event received while not processing a transaction (i.e. no corresponding BEGIN event). " + "This could indicate that your binlog position is invalid.");
                    }
                    // InnoDB generates XID events for "commit", but MyISAM generates Query events with "COMMIT", so handle that here
                    if (includeBeginCommit && (databaseNamePattern == null || databaseNamePattern.matcher(currentDatabase).matches())) {
                        CommitTransactionEventInfo commitTransactionEvent = new CommitTransactionEventInfo(currentDatabase, timestamp, currentBinlogFile, currentBinlogPosition);
                        currentSequenceId.set(commitEventWriter.writeEvent(currentSession, transitUri, commitTransactionEvent, currentSequenceId.get(), REL_SUCCESS));
                    }
                    // Commit the NiFi session
                    session.commit();
                    inTransaction = false;
                    currentTable = null;
                } else {
                    // Check for DDL events (alter table, e.g.). Normalize the query to do string matching on the type of change
                    String normalizedQuery = sql.toLowerCase().trim().replaceAll(" {2,}", " ");
                    if (normalizedQuery.startsWith("alter table") || normalizedQuery.startsWith("alter ignore table") || normalizedQuery.startsWith("create table") || normalizedQuery.startsWith("truncate table") || normalizedQuery.startsWith("rename table") || normalizedQuery.startsWith("drop table") || normalizedQuery.startsWith("drop database")) {
                        if (includeDDLEvents && (databaseNamePattern == null || databaseNamePattern.matcher(currentDatabase).matches())) {
                            // If we don't have table information, we can still use the database name
                            TableInfo ddlTableInfo = (currentTable != null) ? currentTable : new TableInfo(currentDatabase, null, null, null);
                            DDLEventInfo ddlEvent = new DDLEventInfo(ddlTableInfo, timestamp, currentBinlogFile, currentBinlogPosition, sql);
                            currentSequenceId.set(ddlEventWriter.writeEvent(currentSession, transitUri, ddlEvent, currentSequenceId.get(), REL_SUCCESS));
                        }
                        // Remove all the keys from the cache that this processor added
                        if (cacheClient != null) {
                            cacheClient.removeByPattern(this.getIdentifier() + ".*");
                        }
                        // If not in a transaction, commit the session so the DDL event(s) will be transferred
                        if (includeDDLEvents && !inTransaction) {
                            session.commit();
                        }
                    }
                }
                break;
            case XID:
                if (!inTransaction) {
                    throw new IOException("COMMIT event received while not processing a transaction (i.e. no corresponding BEGIN event). " + "This could indicate that your binlog position is invalid.");
                }
                if (includeBeginCommit && (databaseNamePattern == null || databaseNamePattern.matcher(currentDatabase).matches())) {
                    CommitTransactionEventInfo commitTransactionEvent = new CommitTransactionEventInfo(currentDatabase, timestamp, currentBinlogFile, currentBinlogPosition);
                    currentSequenceId.set(commitEventWriter.writeEvent(currentSession, transitUri, commitTransactionEvent, currentSequenceId.get(), REL_SUCCESS));
                }
                // Commit the NiFi session
                session.commit();
                inTransaction = false;
                currentTable = null;
                currentDatabase = null;
                break;
            case WRITE_ROWS:
            case EXT_WRITE_ROWS:
            case PRE_GA_WRITE_ROWS:
            case UPDATE_ROWS:
            case EXT_UPDATE_ROWS:
            case PRE_GA_UPDATE_ROWS:
            case DELETE_ROWS:
            case EXT_DELETE_ROWS:
            case PRE_GA_DELETE_ROWS:
                // If we are skipping this table, then don't emit any events related to its modification
                if (skipTable) {
                    break;
                }
                if (!inTransaction) {
                    // These events should only happen inside a transaction, warn the user otherwise
                    log.warn("Table modification event occurred outside of a transaction.");
                    break;
                }
                if (currentTable == null && cacheClient != null) {
                    // No Table Map event was processed prior to this event, which should not happen, so throw an error
                    throw new RowEventException("No table information is available for this event, cannot process further.");
                }
                if (eventType == WRITE_ROWS || eventType == EXT_WRITE_ROWS || eventType == PRE_GA_WRITE_ROWS) {
                    InsertRowsEventInfo eventInfo = new InsertRowsEventInfo(currentTable, timestamp, currentBinlogFile, currentBinlogPosition, event.getData());
                    currentSequenceId.set(insertRowsWriter.writeEvent(currentSession, transitUri, eventInfo, currentSequenceId.get(), REL_SUCCESS));
                } else if (eventType == DELETE_ROWS || eventType == EXT_DELETE_ROWS || eventType == PRE_GA_DELETE_ROWS) {
                    DeleteRowsEventInfo eventInfo = new DeleteRowsEventInfo(currentTable, timestamp, currentBinlogFile, currentBinlogPosition, event.getData());
                    currentSequenceId.set(deleteRowsWriter.writeEvent(currentSession, transitUri, eventInfo, currentSequenceId.get(), REL_SUCCESS));
                } else {
                    // Update event
                    UpdateRowsEventInfo eventInfo = new UpdateRowsEventInfo(currentTable, timestamp, currentBinlogFile, currentBinlogPosition, event.getData());
                    currentSequenceId.set(updateRowsWriter.writeEvent(currentSession, transitUri, eventInfo, currentSequenceId.get(), REL_SUCCESS));
                }
                break;
            case ROTATE:
                // Update current binlog filename
                RotateEventData rotateEventData = event.getData();
                currentBinlogFile = rotateEventData.getBinlogFilename();
                currentBinlogPosition = rotateEventData.getBinlogPosition();
                break;
            default:
                break;
        }
        // advance the position if it is not that type of event.
        if (eventType != ROTATE && eventType != FORMAT_DESCRIPTION) {
            currentBinlogPosition = header.getNextPosition();
        }
    }
}
Also used : QueryEventData(com.github.shyiko.mysql.binlog.event.QueryEventData) EventType(com.github.shyiko.mysql.binlog.event.EventType) SQLException(java.sql.SQLException) IOException(java.io.IOException) UpdateRowsEventInfo(org.apache.nifi.cdc.mysql.event.UpdateRowsEventInfo) RotateEventData(com.github.shyiko.mysql.binlog.event.RotateEventData) RawBinlogEvent(org.apache.nifi.cdc.mysql.event.RawBinlogEvent) DeleteRowsEventInfo(org.apache.nifi.cdc.mysql.event.DeleteRowsEventInfo) CommitTransactionEventInfo(org.apache.nifi.cdc.mysql.event.CommitTransactionEventInfo) TableMapEventData(com.github.shyiko.mysql.binlog.event.TableMapEventData) BeginTransactionEventInfo(org.apache.nifi.cdc.mysql.event.BeginTransactionEventInfo) DDLEventInfo(org.apache.nifi.cdc.mysql.event.DDLEventInfo) RowEventException(org.apache.nifi.cdc.event.RowEventException) RawBinlogEvent(org.apache.nifi.cdc.mysql.event.RawBinlogEvent) Event(com.github.shyiko.mysql.binlog.event.Event) EventHeaderV4(com.github.shyiko.mysql.binlog.event.EventHeaderV4) TableInfo(org.apache.nifi.cdc.event.TableInfo) TableInfoCacheKey(org.apache.nifi.cdc.event.TableInfoCacheKey) InsertRowsEventInfo(org.apache.nifi.cdc.mysql.event.InsertRowsEventInfo) ConnectException(java.net.ConnectException)

Aggregations

QueryEventData (com.github.shyiko.mysql.binlog.event.QueryEventData)5 EventHeaderV4 (com.github.shyiko.mysql.binlog.event.EventHeaderV4)2 EventType (com.github.shyiko.mysql.binlog.event.EventType)2 TableMapEventData (com.github.shyiko.mysql.binlog.event.TableMapEventData)2 DeleteEvent (com.airbnb.spinaltap.mysql.event.DeleteEvent)1 QueryEvent (com.airbnb.spinaltap.mysql.event.QueryEvent)1 StartEvent (com.airbnb.spinaltap.mysql.event.StartEvent)1 TableMapEvent (com.airbnb.spinaltap.mysql.event.TableMapEvent)1 UpdateEvent (com.airbnb.spinaltap.mysql.event.UpdateEvent)1 WriteEvent (com.airbnb.spinaltap.mysql.event.WriteEvent)1 XidEvent (com.airbnb.spinaltap.mysql.event.XidEvent)1 DeleteRowsEventData (com.github.shyiko.mysql.binlog.event.DeleteRowsEventData)1 Event (com.github.shyiko.mysql.binlog.event.Event)1 RotateEventData (com.github.shyiko.mysql.binlog.event.RotateEventData)1 UpdateRowsEventData (com.github.shyiko.mysql.binlog.event.UpdateRowsEventData)1 WriteRowsEventData (com.github.shyiko.mysql.binlog.event.WriteRowsEventData)1 XidEventData (com.github.shyiko.mysql.binlog.event.XidEventData)1 IOException (java.io.IOException)1 ConnectException (java.net.ConnectException)1 SQLException (java.sql.SQLException)1