All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.debezium.relational.RelationalChangeRecordEmitter Maven / Gradle / Ivy

/*
 * Copyright Debezium Authors.
 *
 * Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
 */

package io.debezium.relational;

import io.debezium.data.Envelope.Operation;
import io.debezium.pipeline.AbstractChangeRecordEmitter;
import io.debezium.pipeline.spi.ChangeRecordEmitter;
import io.debezium.pipeline.spi.OffsetContext;
import io.debezium.pipeline.spi.Partition;
import io.debezium.schema.DataCollectionSchema;
import io.debezium.util.Clock;
import org.apache.kafka.connect.data.Struct;
import org.apache.kafka.connect.header.ConnectHeaders;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;
import java.util.Optional;

/**
 * Copied from Debezium 1.9.8.Final.
 *
 * 

Base class for {@link ChangeRecordEmitter} implementations based on a relational database. * *

This class overrides the emit methods to put some values in the header. * *

Line 59 ~ 257: add other headers and emit. */ public abstract class RelationalChangeRecordEmitter

extends AbstractChangeRecordEmitter { private static final Logger LOGGER = LoggerFactory.getLogger(RelationalChangeRecordEmitter.class); public static final String PK_UPDATE_OLDKEY_FIELD = "__debezium.oldkey"; public static final String PK_UPDATE_NEWKEY_FIELD = "__debezium.newkey"; public RelationalChangeRecordEmitter(P partition, OffsetContext offsetContext, Clock clock) { super(partition, offsetContext, clock); } @Override public void emitChangeRecords(DataCollectionSchema schema, Receiver

receiver) throws InterruptedException { TableSchema tableSchema = (TableSchema) schema; Operation operation = getOperation(); switch (operation) { case CREATE: emitCreateRecord(receiver, tableSchema); break; case READ: emitReadRecord(receiver, tableSchema); break; case UPDATE: emitUpdateRecord(receiver, tableSchema); break; case DELETE: emitDeleteRecord(receiver, tableSchema); break; case TRUNCATE: emitTruncateRecord(receiver, tableSchema); break; default: throw new IllegalArgumentException("Unsupported operation: " + operation); } } @Override protected void emitCreateRecord(Receiver

receiver, TableSchema tableSchema) throws InterruptedException { Object[] newColumnValues = getNewColumnValues(); Struct newKey = tableSchema.keyFromColumnData(newColumnValues); Struct newValue = tableSchema.valueFromColumnData(newColumnValues); Struct envelope = tableSchema .getEnvelopeSchema() .create( newValue, getOffset().getSourceInfo(), getClock().currentTimeAsInstant()); if (skipEmptyMessages() && (newColumnValues == null || newColumnValues.length == 0)) { // This case can be hit on UPDATE / DELETE when there's no primary key defined while // using certain decoders LOGGER.warn( "no new values found for table '{}' from create message at '{}'; skipping record", tableSchema, getOffset().getSourceInfo()); return; } receiver.changeRecord( getPartition(), tableSchema, Operation.CREATE, newKey, envelope, getOffset(), getEmitConnectHeaders().orElse(null)); } @Override protected void emitReadRecord(Receiver

receiver, TableSchema tableSchema) throws InterruptedException { Object[] newColumnValues = getNewColumnValues(); Struct newKey = tableSchema.keyFromColumnData(newColumnValues); Struct newValue = tableSchema.valueFromColumnData(newColumnValues); Struct envelope = tableSchema .getEnvelopeSchema() .read( newValue, getOffset().getSourceInfo(), getClock().currentTimeAsInstant()); receiver.changeRecord( getPartition(), tableSchema, Operation.READ, newKey, envelope, getOffset(), getEmitConnectHeaders().orElse(null)); } @Override protected void emitUpdateRecord(Receiver

receiver, TableSchema tableSchema) throws InterruptedException { Object[] oldColumnValues = getOldColumnValues(); Object[] newColumnValues = getNewColumnValues(); Struct oldKey = tableSchema.keyFromColumnData(oldColumnValues); Struct newKey = tableSchema.keyFromColumnData(newColumnValues); Struct newValue = tableSchema.valueFromColumnData(newColumnValues); Struct oldValue = tableSchema.valueFromColumnData(oldColumnValues); if (skipEmptyMessages() && (newColumnValues == null || newColumnValues.length == 0)) { LOGGER.warn( "no new values found for table '{}' from update message at '{}'; skipping record", tableSchema, getOffset().getSourceInfo()); return; } // some configurations does not provide old values in case of updates // in this case we handle all updates as regular ones if (oldKey == null || Objects.equals(oldKey, newKey)) { Struct envelope = tableSchema .getEnvelopeSchema() .update( oldValue, newValue, getOffset().getSourceInfo(), getClock().currentTimeAsInstant()); receiver.changeRecord( getPartition(), tableSchema, Operation.UPDATE, newKey, envelope, getOffset(), getEmitConnectHeaders().orElse(null)); } // PK update -> emit as delete and re-insert with new key else { emitUpdateAsPrimaryKeyChangeRecord( receiver, tableSchema, oldKey, newKey, oldValue, newValue); } } @Override protected void emitDeleteRecord(Receiver

receiver, TableSchema tableSchema) throws InterruptedException { Object[] oldColumnValues = getOldColumnValues(); Struct oldKey = tableSchema.keyFromColumnData(oldColumnValues); Struct oldValue = tableSchema.valueFromColumnData(oldColumnValues); if (skipEmptyMessages() && (oldColumnValues == null || oldColumnValues.length == 0)) { LOGGER.warn( "no old values found for table '{}' from delete message at '{}'; skipping record", tableSchema, getOffset().getSourceInfo()); return; } Struct envelope = tableSchema .getEnvelopeSchema() .delete( oldValue, getOffset().getSourceInfo(), getClock().currentTimeAsInstant()); receiver.changeRecord( getPartition(), tableSchema, Operation.DELETE, oldKey, envelope, getOffset(), getEmitConnectHeaders().orElse(null)); } protected void emitTruncateRecord(Receiver

receiver, TableSchema schema) throws InterruptedException { throw new UnsupportedOperationException("TRUNCATE not supported"); } /** Returns the old row state in case of an UPDATE or DELETE. */ protected abstract Object[] getOldColumnValues(); /** Returns the new row state in case of a CREATE or READ. */ protected abstract Object[] getNewColumnValues(); /** * Whether empty data messages should be ignored. * * @return true if empty data messages coming from data source should be ignored. Typical use * case are PostgreSQL changes without FULL replica identity. */ protected boolean skipEmptyMessages() { return false; } protected void emitUpdateAsPrimaryKeyChangeRecord( Receiver

receiver, TableSchema tableSchema, Struct oldKey, Struct newKey, Struct oldValue, Struct newValue) throws InterruptedException { ConnectHeaders headers = getEmitConnectHeaders().orElse(new ConnectHeaders()); headers.add(PK_UPDATE_NEWKEY_FIELD, newKey, tableSchema.keySchema()); Struct envelope = tableSchema .getEnvelopeSchema() .delete( oldValue, getOffset().getSourceInfo(), getClock().currentTimeAsInstant()); receiver.changeRecord( getPartition(), tableSchema, Operation.DELETE, oldKey, envelope, getOffset(), headers); headers = getEmitConnectHeaders().orElse(new ConnectHeaders()); headers.add(PK_UPDATE_OLDKEY_FIELD, oldKey, tableSchema.keySchema()); envelope = tableSchema .getEnvelopeSchema() .create( newValue, getOffset().getSourceInfo(), getClock().currentTimeAsInstant()); receiver.changeRecord( getPartition(), tableSchema, Operation.CREATE, newKey, envelope, getOffset(), headers); } protected Optional getEmitConnectHeaders() { return Optional.empty(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy