package com.zendesk.maxwell.replication;
import java.util.*;
import com.google.code.or.binlog.BinlogEventV4Header;
import com.google.code.or.binlog.impl.event.AbstractRowEvent;
import com.google.code.or.common.glossary.Column;
import com.google.code.or.common.glossary.Row;
import com.google.code.or.common.glossary.column.BitColumn;
import com.zendesk.maxwell.MaxwellFilter;
import com.zendesk.maxwell.row.RowMap;
import com.zendesk.maxwell.schema.ColumnWithDefinition;
import com.zendesk.maxwell.schema.ColumnWithDefinitionList;
import com.zendesk.maxwell.schema.Table;
import com.zendesk.maxwell.schema.columndef.ColumnDef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// the main wrapper for a raw (AbstractRowEvent) binlog event.
// decorates the event with metadata info from Table,
// filters rows using the passed in MaxwellFilter,
// and ultimately outputs arrays of RowMap objects.
public abstract class AbstractRowsEvent extends AbstractRowEvent {
static final Logger LOGGER = LoggerFactory.getLogger(AbstractRowsEvent.class);
private final AbstractRowEvent event;
protected final Table table;
protected final String database;
protected final MaxwellFilter filter;
private final long lastHeartbeatRead;
public AbstractRowsEvent(AbstractRowEvent e, Table table, MaxwellFilter f, long lastHeartbeat) {
this.tableId = e.getTableId();
this.event = e;
this.header = e.getHeader();
this.table = table;
this.database = table.getDatabase();
this.filter = f;
this.lastHeartbeatRead = lastHeartbeat;
}
@Override
public BinlogEventV4Header getHeader() {
return event.getHeader();
}
public Table getTable() {
return table;
}
public String getDatabase() {
return database;
}
@Override
public String getBinlogFilename() {
return event.getBinlogFilename();
}
public Position getNextPosition() {
return new Position(new BinlogPosition(getHeader().getNextPosition(), getBinlogFilename()), lastHeartbeatRead);
}
@Override
public void setBinlogFilename(String binlogFilename) {
event.setBinlogFilename(binlogFilename);
}
public boolean matchesFilter() {
return MaxwellFilter.matches(filter, this.database, this.table.getName());
}
public abstract String getType();
public Column findColumn(String name, Row r) {
int i = table.findColumnIndex(name);
if ( i > 0 )
return r.getColumns().get(i);
else
return null;
}
@Override
public String toString() {
return event.toString();
}
public abstract List<Row> getRows();
public abstract String sqlOperationString();
private void appendColumnNames(StringBuilder sql)
{
sql.append(" (");
for(Iterator<ColumnDef> i = table.getColumnList().iterator(); i.hasNext();) {
ColumnDef c = i.next();
sql.append("`" + c.getName() + "`");
if ( i.hasNext() )
sql.append(", ");
}
sql.append(")");
}
public String toSQL() {
StringBuilder sql = new StringBuilder();
List<Row> rows = getRows();
if ( rows.isEmpty() )
return null;
sql.append(sqlOperationString());
sql.append("`" + table.getName() + "`");
appendColumnNames(sql);
sql.append(" VALUES ");
Iterator<Row> rowIter = rows.iterator();
while(rowIter.hasNext()) {
Row row = rowIter.next();
sql.append("(");
Iterator<Column> colIter = row.getColumns().iterator();
Iterator<ColumnDef> defIter = table.getColumnList().iterator();
while ( colIter.hasNext() && defIter.hasNext() ) {
Column c = colIter.next();
ColumnDef d = defIter.next();
sql.append(d.toSQL(c.getValue()));
if (colIter.hasNext())
sql.append(",");
}
if ( rowIter.hasNext() ) {
sql.append("),");
} else {
sql.append(")");
}
}
return sql.toString();
}
protected RowMap buildRowMap() {
return new RowMap(
getType(),
this.database,
getTable().getName(),
getHeader().getTimestamp() / 1000,
table.getPKList(),
this.getNextPosition());
}
public List<RowMap> jsonMaps() {
ArrayList<RowMap> list = new ArrayList<>();
for ( Row r : getRows() ) {
RowMap rowMap = buildRowMap();
for ( ColumnWithDefinition cd : new ColumnWithDefinitionList(table, r, getUsedColumns()) )
rowMap.putData(cd.definition.getName(), cd.asJSON());
list.add(rowMap);
}
return list;
}
protected abstract BitColumn getUsedColumns();
}