package com.zendesk.maxwell.schema; import com.zendesk.maxwell.CaseSensitivity; import com.zendesk.maxwell.MaxwellContext; import com.zendesk.maxwell.replication.BinlogPosition; import com.zendesk.maxwell.MaxwellFilter; import com.zendesk.maxwell.replication.Position; import com.zendesk.maxwell.schema.ddl.InvalidSchemaError; import com.zendesk.maxwell.schema.ddl.ResolvedSchemaChange; import snaq.db.ConnectionPool; import java.sql.Connection; import java.sql.SQLException; import java.util.Collections; import java.util.List; import static com.zendesk.maxwell.schema.MysqlSavedSchema.restore; public class MysqlSchemaStore extends AbstractSchemaStore implements SchemaStore { private final ConnectionPool maxwellConnectionPool; private final Position initialPosition; private final boolean readOnly; private final MaxwellFilter filter; private Long serverID; private MysqlSavedSchema savedSchema; public MysqlSchemaStore(ConnectionPool maxwellConnectionPool, ConnectionPool replicationConnectionPool, ConnectionPool schemaConnectionPool, Long serverID, Position initialPosition, CaseSensitivity caseSensitivity, MaxwellFilter filter, boolean readOnly) { super(replicationConnectionPool, schemaConnectionPool, caseSensitivity, filter); this.serverID = serverID; this.filter = filter; this.maxwellConnectionPool = maxwellConnectionPool; this.initialPosition = initialPosition; this.readOnly = readOnly; } public MysqlSchemaStore(MaxwellContext context, Position initialPosition) throws SQLException { this( context.getMaxwellConnectionPool(), context.getReplicationConnectionPool(), context.getSchemaConnectionPool(), context.getServerID(), initialPosition, context.getCaseSensitivity(), context.getFilter(), context.getReplayMode() ); } public Schema getSchema() throws SchemaStoreException { if ( savedSchema == null ) savedSchema = restoreOrCaptureSchema(); return savedSchema.getSchema(); } private MysqlSavedSchema restoreOrCaptureSchema() throws SchemaStoreException { try ( Connection conn = maxwellConnectionPool.getConnection() ) { MysqlSavedSchema savedSchema = restore(maxwellConnectionPool, serverID, caseSensitivity, initialPosition); if ( savedSchema == null ) { Schema capturedSchema = captureSchema(); savedSchema = new MysqlSavedSchema(serverID, caseSensitivity, capturedSchema, initialPosition); if (!readOnly) if (conn.isValid(30)) { savedSchema.save(conn); } else { // The capture time might be long and the conn connection might be closed already. Consulting the pool // again for a new connection Connection newConn = maxwellConnectionPool.getConnection(); savedSchema.save(newConn); } } return savedSchema; } catch (SQLException e) { throw new SchemaStoreException(e); } catch (InvalidSchemaError e) { throw new SchemaStoreException(e); } } public List<ResolvedSchemaChange> processSQL(String sql, String currentDatabase, Position position) throws SchemaStoreException, InvalidSchemaError { List<ResolvedSchemaChange> resolvedSchemaChanges = resolveSQL(getSchema(), sql, currentDatabase); if ( resolvedSchemaChanges.size() > 0 ) { try { Long schemaId = saveSchema(getSchema(), resolvedSchemaChanges, position); LOGGER.info("storing schema @" + position + " after applying \"" + sql.replace('\n', ' ') + "\" to " + currentDatabase + ", new schema id is " + schemaId); } catch (SQLException e) { throw new SchemaStoreException(e); } } return resolvedSchemaChanges; } private Long saveSchema(Schema updatedSchema, List<ResolvedSchemaChange> changes, Position p) throws SQLException { if ( readOnly ) return null; try (Connection c = maxwellConnectionPool.getConnection()) { this.savedSchema = this.savedSchema.createDerivedSchema(updatedSchema, p, changes); return this.savedSchema.save(c); } } public void clone(Long serverID, Position position) throws SchemaStoreException { List<ResolvedSchemaChange> empty = Collections.emptyList(); try (Connection c = maxwellConnectionPool.getConnection()) { getSchema(); MysqlSavedSchema cloned = new MysqlSavedSchema(serverID, caseSensitivity, getSchema(), position, savedSchema.getSchemaID(), empty); Long schemaId = cloned.save(c); LOGGER.info("clone schema @" + position + " based on id " + savedSchema.getSchemaID() + ", new schema id is " + schemaId); } catch ( SQLException e ) { throw new SchemaStoreException(e); } } }