DBZ-252 add javadoc

This commit is contained in:
rkuchar 2018-05-09 21:51:32 +02:00 committed by Gunnar Morling
parent d59506ad31
commit 5022933581
24 changed files with 234 additions and 30 deletions

View File

@ -12,6 +12,8 @@
import java.util.concurrent.ConcurrentMap;
/**
* Custom class for MySQL {@link SystemVariables}, which defines MySQL scopes and constants of used variable names.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class MySqlSystemVariables extends SystemVariables {
@ -26,7 +28,6 @@ public enum MySqlScope implements Scope {
this.priority = priority;
}
@Override
public int priority() {
return priority;

View File

@ -24,6 +24,7 @@
import java.util.Arrays;
import java.util.List;
import io.debezium.connector.mysql.antlr.MySqlAntlrDdlParser;
import org.apache.kafka.connect.data.Decimal;
import org.apache.kafka.connect.data.Field;
import org.apache.kafka.connect.data.Schema;
@ -502,7 +503,8 @@ protected boolean matches(String upperCaseTypeName, String upperCaseMatch) {
}
protected List<String> extractEnumAndSetOptions(Column column) {
return MySqlDdlParser.parseSetAndEnumOptions(column.typeExpression());
// return MySqlDdlParser.parseSetAndEnumOptions(column.typeExpression());
return MySqlAntlrDdlParser.parseSetAndEnumOptions(column.typeExpression());
}
protected String extractEnumAndSetOptionsAsString(Column column) {

View File

@ -33,6 +33,8 @@
import java.util.stream.Collectors;
/**
* A ANTLR based parser for MySQL DDL statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class MySqlAntlrDdlParser extends AntlrDdlParser<MySqlLexer, MySqlParser> {
@ -175,14 +177,32 @@ protected void initDataTypes(DataTypeResolver dataTypeResolver) {
));
}
/**
* Provides a map of default character sets by database/schema name.
*
* @return map of default character sets.
*/
public ConcurrentMap<String, String> charsetNameForDatabase() {
return charsetNameForDatabase;
}
/**
* Parse a name from {@link MySqlParser.UidContext}.
*
* @param uidContext uid context
* @return name without quotes.
*/
public String parseName(MySqlParser.UidContext uidContext) {
return withoutQuotes(uidContext);
}
/**
* Parse qualified table identification from {@link MySqlParser.FullIdContext}.
* {@link MySqlAntlrDdlParser#currentSchema()} will be used if definition of schema name is not part of the context.
*
* @param fullIdContext full id context.
* @return qualified {@link TableId}.
*/
public TableId parseQualifiedTableId(MySqlParser.FullIdContext fullIdContext) {
String fullTableName = fullIdContext.getText();
int dotIndex;
@ -195,6 +215,13 @@ public TableId parseQualifiedTableId(MySqlParser.FullIdContext fullIdContext) {
}
}
/**
* Parse column names for primary index from {@link MySqlParser.IndexColumnNamesContext}. This method will updates
* column to be not optional and set primary key column names to table.
*
* @param indexColumnNamesContext primary key index column names context.
* @param tableEditor editor for table where primary key index is parsed.
*/
public void parsePrimaryIndexColumnNames(MySqlParser.IndexColumnNamesContext indexColumnNamesContext, TableEditor tableEditor) {
List<String> pkColumnNames = indexColumnNamesContext.indexColumnName().stream()
.map(indexColumnNameContext -> {
@ -230,15 +257,12 @@ public String currentDatabaseCharset() {
return charsetName;
}
public String getFullTableName(TableId tableId) {
if (tableId.catalog() != null) {
return tableId.catalog() + "." + tableId.table();
}
else {
return tableId.table();
}
}
/**
* Runs a function if all given object are not null.
*
* @param function function to run; may not be null
* @param nullableObjects object to be tested, if they are null.
*/
public void runIfNotNull(Runnable function, Object... nullableObjects) {
for (Object nullableObject : nullableObjects) {
if (nullableObject == null) {

View File

@ -22,6 +22,8 @@
import static io.debezium.antlr.AntlrDdlParser.getText;
/**
* Parser listeners that is parsing MySQL ALTER TABLE statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class AlterTableParserListener extends MySqlParserBaseListener {
@ -47,7 +49,7 @@ public void enterAlterTable(MySqlParser.AlterTableContext ctx) {
TableId tableId = parserCtx.parseQualifiedTableId(ctx.tableName().fullId());
tableEditor = parserCtx.databaseTables().editTable(tableId);
if (tableEditor == null) {
throw new ParsingException(null, "Trying to alter table " + parserCtx.getFullTableName(tableId)
throw new ParsingException(null, "Trying to alter table " + tableId.toString()
+ ", which does not exists. Query: " + getText(ctx));
}
super.enterAlterTable(ctx);
@ -146,7 +148,7 @@ public void enterAlterByChangeColumn(MySqlParser.AlterByChangeColumnContext ctx)
}
else {
throw new ParsingException(null, "Trying to change column " + oldColumnName + " in "
+ parserCtx.getFullTableName(tableEditor.tableId()) + " table, which does not exists. Query: " + getText(ctx));
+ tableEditor.tableId().toString() + " table, which does not exists. Query: " + getText(ctx));
}
}, tableEditor);
super.enterAlterByChangeColumn(ctx);
@ -181,7 +183,7 @@ public void enterAlterByModifyColumn(MySqlParser.AlterByModifyColumnContext ctx)
}
else {
throw new ParsingException(null, "Trying to change column " + columnName + " in "
+ parserCtx.getFullTableName(tableEditor.tableId()) + " table, which does not exists. Query: " + getText(ctx));
+ tableEditor.tableId().toString() + " table, which does not exists. Query: " + getText(ctx));
}
}, tableEditor);
super.enterAlterByModifyColumn(ctx);

View File

@ -19,6 +19,8 @@
import java.util.List;
/**
* Parser listeners that is parsing MySQL ALTER VIEW statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class AlterViewParserListener extends MySqlParserBaseListener {
@ -42,7 +44,7 @@ public void enterAlterView(MySqlParser.AlterViewContext ctx) {
tableEditor = parserCtx.databaseTables().editTable(tableId);
if (tableEditor == null) {
throw new ParsingException(null, "Trying to alter view " + parserCtx.getFullTableName(tableId)
throw new ParsingException(null, "Trying to alter view " + tableId.toString()
+ ", which does not exists. Query:" + AntlrDdlParser.getText(ctx));
}
// alter view will override existing columns for a new one

View File

@ -20,6 +20,8 @@
import static io.debezium.antlr.AntlrDdlParser.getText;
/**
* Parser listeners that is parsing column definition part of MySQL statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class ColumnDefinitionParserListener extends MySqlParserBaseListener {

View File

@ -12,7 +12,8 @@
import io.debezium.ddl.parser.mysql.generated.MySqlParserBaseListener;
/**
* Parser listener for MySQL create database query to get database charsetName.
* Parser listeners that is parsing MySQL CREATE DATABASE and ALTER DATABASE statements,
* to get default character sets for database.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/

View File

@ -19,6 +19,8 @@
import java.util.List;
/**
* Parser listeners that is parsing MySQL CREATE TABLE statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class CreateTableParserListener extends MySqlParserBaseListener {

View File

@ -16,6 +16,9 @@
import static io.debezium.antlr.AntlrDdlParser.getText;
/**
* Parser listeners that is parsing MySQL CREATE UNIQUE INDEX statements, that will be used as a primary key
* if it's not already defined for the table.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class CreateUniqueIndexParserListener extends MySqlParserBaseListener {
@ -39,7 +42,7 @@ public void enterCreateIndex(MySqlParser.CreateIndexContext ctx) {
}
}
else {
throw new ParsingException(null, "Trying to create index on non existing table " + parserCtx.getFullTableName(tableId) + "."
throw new ParsingException(null, "Trying to create index on non existing table " + tableId.toString() + "."
+ "Query: " + getText(ctx));
}
}

View File

@ -16,6 +16,8 @@
import java.util.List;
/**
* Parser listeners that is parsing MySQL CREATE VIEW statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class CreateViewParserListener extends MySqlParserBaseListener {

View File

@ -11,6 +11,8 @@
import io.debezium.ddl.parser.mysql.generated.MySqlParserBaseListener;
/**
* Parser listeners that is parsing MySQL DROP DATABASE statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class DropDatabaseParserListener extends MySqlParserBaseListener {

View File

@ -13,6 +13,8 @@
import org.antlr.v4.runtime.misc.Interval;
/**
* Parser listeners that is parsing MySQL DROP TABLE statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class DropTableParserListener extends MySqlParserBaseListener {

View File

@ -11,6 +11,8 @@
import io.debezium.ddl.parser.mysql.generated.MySqlParserBaseListener;
/**
* Parser listeners that is parsing MySQL DROP VIEW statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class DropViewParserListener extends MySqlParserBaseListener {

View File

@ -6,10 +6,6 @@
package io.debezium.connector.mysql.antlr.listener;
/**
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
import io.debezium.antlr.AntlrDdlParserListener;
import io.debezium.antlr.ProxyParseTreeListenerUtil;
import io.debezium.connector.mysql.antlr.MySqlAntlrDdlParser;
@ -27,17 +23,36 @@
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Parser listener for MySQL column definition queries.
* Parser listener for MySQL column definition queries. It's purpose is to delegate events
* to defined collection of concrete parser listeners. Each listener handles the specified type of DDL statement.
* This listener is catching all occurred parsing exceptions and implements a skipping logic for BEGIN ... END
* statements. No event will be delegated during skipping phase.
*/
public class MySqlAntlrDdlParserListener extends MySqlParserBaseListener implements AntlrDdlParserListener {
/**
* Collection of listeners for delegation of events.
*/
private List<ParseTreeListener> listeners = new CopyOnWriteArrayList<>();
/**
* Flag for skipping phase.
*/
private boolean skipNodes;
/**
* Count of skipped nodes. Each enter event during skipping phase will increase the counter
* and each exit event will decrease it. When counter will be decreased to 0, the skipping phase will end.
*/
private int skippedNodesCount = 0;
/**
* Collection of catched exceptions.
*/
private Collection<ParsingException> errors = new ArrayList<>();
public MySqlAntlrDdlParserListener(MySqlAntlrDdlParser parserCtx) {
// initialize listeners
listeners.add(new CreateAndAlterDatabaseParserListener(parserCtx));
listeners.add(new DropDatabaseParserListener(parserCtx));
listeners.add(new CreateTableParserListener(parserCtx, listeners));
@ -101,6 +116,7 @@ public void visitTerminal(TerminalNode node) {
@Override
public void enterRoutineBody(MySqlParser.RoutineBodyContext ctx) {
// this is a grammar rule for BEGIN ... END part of statements. Skip it.
skipNodes = true;
}

View File

@ -12,6 +12,8 @@
import io.debezium.relational.TableId;
/**
* Parser listeners that is parsing MySQL RENAME TABLE statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class RenameTableParserListener extends MySqlParserBaseListener {

View File

@ -12,6 +12,8 @@
import io.debezium.ddl.parser.mysql.generated.MySqlParserBaseListener;
/**
* Parser listeners that is parsing MySQL SET statements, for defining a system variables.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class SetStatementParserListener extends MySqlParserBaseListener {

View File

@ -12,6 +12,8 @@
import io.debezium.relational.TableId;
/**
* Parser listeners that is parsing MySQL TRUNCATE TABLE statements.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class TruncateTableParserListener extends MySqlParserBaseListener {

View File

@ -12,6 +12,9 @@
import io.debezium.ddl.parser.mysql.generated.MySqlParserBaseListener;
/**
* Parser listeners that is parsing MySQL USE statements that changes
* current database/schema on which all changes are applied.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class UseStatementParserListener extends MySqlParserBaseListener {

View File

@ -21,6 +21,8 @@
import static io.debezium.relational.ddl.AbstractDdlParser.withoutQuotes;
/**
* Parser listeners that is parsing MySQL SELECT statements used for definition of VIEW.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class ViewSelectedColumnsParserListener extends MySqlParserBaseListener {

View File

@ -26,11 +26,26 @@
import java.util.Collection;
/**
* Base implementation of ANTLR based parsers.
*
* This abstract class provides generic initialization of parser and it's main sequence of steps
* that are needed to properly start parsing.
* It also provides implementation of helper methods for any type of ANTLR listeners.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public abstract class AntlrDdlParser<L extends Lexer, P extends Parser> extends AbstractDdlParser {
/**
* Flag to indicate if the errors catched during tree walk will be thrown.
* true = errors will be thrown
* false = errors will not be thrown. They will be available to get by {@link AntlrDdlParser#getParsingExceptionsFromWalker()}.
*/
private boolean throwErrorsFromTreeWalk;
/**
* Parser listener for tree walker.
*/
private AntlrDdlParserListener antlrDdlParserListener;
protected Tables databaseTables;
@ -78,6 +93,11 @@ public void parse(String ddlContent, Tables databaseTables) {
}
}
/**
* Returns errors catched during tree walk.
*
* @return collection of {@link ParsingException}s.
*/
public Collection<ParsingException> getParsingExceptionsFromWalker() {
return antlrDdlParserListener.getErrors();
}
@ -90,6 +110,11 @@ public Collection<ParsingException> getParsingExceptionsFromWalker() {
*/
protected abstract ParseTree parseTree(P parser);
/**
* Creates a new instance of parsed tree walker listener.
*
* @return new instance of parser listener.
*/
protected abstract AntlrDdlParserListener createParseTreeWalkerListener();
/**
@ -122,10 +147,20 @@ public Collection<ParsingException> getParsingExceptionsFromWalker() {
*/
protected abstract void initDataTypes(DataTypeResolver dataTypeResolver);
/**
* Returns actual tables schema.
*
* @return table schema.
*/
public Tables databaseTables() {
return databaseTables;
}
/**
* Returns a data type resolver component.
*
* @return data type resolver.
*/
public DataTypeResolver dataTypeResolver() {
return dataTypeResolver;
}

View File

@ -8,14 +8,22 @@
import io.debezium.text.ParsingException;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import java.util.Collection;
/**
* Interface for listeners used by {@link ParseTreeWalker}.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public interface AntlrDdlParserListener extends ParseTreeListener {
/**
* Returns errors that occurred during parsed tree walk.
*
* @return collection of {@link ParsingException}s.
*/
Collection<ParsingException> getErrors();
}

View File

@ -17,19 +17,45 @@
import java.util.List;
import java.util.Map;
/**
* A resolver for DBMS data types.
*
* It's main purpose is to match corresponding JDBC data type, resolve a name of parsed data type,
* and optionally predefine default values for length and scale for DBMS data type.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class DataTypeResolver {
private final Map<String, List<DataTypeEntry>> contextDataTypesMap = new HashMap<>();
/**
* Registers a data type entries, which will be used for resolving phase.
*
* @param contextClassCanonicalName canonical name of context instance, in which the data type entry will appear; may not be null
* @param dataTypeEntries list of {@link DataTypeEntry} definitions; may not be null
*/
public void registerDataTypes(String contextClassCanonicalName, List<DataTypeEntry> dataTypeEntries) {
contextDataTypesMap.put(contextClassCanonicalName, dataTypeEntries);
}
public void registerDataTypes(String contextClassCanonicalName, DataTypeEntry dataTypeEntry) {
/**
* Registers a data type entry, which will be used for resolving phase.
*
* @param contextClassCanonicalName canonical name of context instance, in which the data type entry will appear; may not be null
* @param dataTypeEntry {@link DataTypeEntry} definitions; may not be null
*/
public void registerDataType(String contextClassCanonicalName, DataTypeEntry dataTypeEntry) {
List<DataTypeEntry> dataTypeEntries = contextDataTypesMap.computeIfAbsent(contextClassCanonicalName, k -> new ArrayList<>());
dataTypeEntries.add(dataTypeEntry);
}
/**
* Resolves a data type from given parsed context.
*
* @param dataTypeContext parse context; may not e null
* @return instance of {@link DataType}, which will holds matched JDBC type, name and default values for length and scale.
*/
public DataType resolveDataType(ParserRuleContext dataTypeContext) {
DataType dataType = null;
// use priority according to number of matched tokens
@ -81,17 +107,22 @@ private void addOptionalSuffixToName(ParserRuleContext dataTypeContext, DataType
}
}
/**
* DTO class for definition of data type.
*/
public static class DataTypeEntry {
/**
* Mapped JDBC data type
* The corresponding JDBC data type
*/
private final int jdbcDataType;
/**
* Token identifiers for DBMS data type
*/
private final Integer[] dbmsDataTypeTokenIdentifiers;
/**
* Token identifiers for optional suffix tokens for DBMS data type.
*/
private Integer[] suffixTokens = null;
private int defaultLength = -1;
private int defaultScale = -1;
@ -101,36 +132,56 @@ public DataTypeEntry(int jdbcDataType, Integer... dbmsDataTypeTokenIdentifiers)
this.jdbcDataType = jdbcDataType;
}
public Integer[] getDbmsDataTypeTokenIdentifiers() {
Integer[] getDbmsDataTypeTokenIdentifiers() {
return dbmsDataTypeTokenIdentifiers;
}
public int getJdbcDataType() {
int getJdbcDataType() {
return jdbcDataType;
}
public Integer[] getSuffixTokens() {
Integer[] getSuffixTokens() {
return suffixTokens;
}
public int getDefaultLength() {
int getDefaultLength() {
return defaultLength;
}
public int getDefaultScale() {
int getDefaultScale() {
return defaultScale;
}
/**
* Sets an optional suffix tokens that may appear in DBMS data type definition.
*
* @param suffixTokens optional suffix tokens.
* @return instance of this class, so the calls may be chained.
*/
public DataTypeEntry setSuffixTokens(Integer... suffixTokens) {
this.suffixTokens = suffixTokens;
return this;
}
/**
* Set a default length for data type.
*
* @param defaultLength default length for data type.
* @return instance of this class, so the calls may be chained.
*/
public DataTypeEntry setDefualtLengthDimmension(int defaultLength) {
this.defaultLength = defaultLength;
return this;
}
/**
* Set a default length and scale for data type.
*
* @param defaultLength default length for data type.
* @param defaultScale default scale for data type.
* @return instance of this class, so the calls may be chained.
*/
public DataTypeEntry setDefaultLengthScaleDimension(int defaultLength, int defaultScale) {
this.defaultLength = defaultLength;
this.defaultScale = defaultScale;

View File

@ -18,6 +18,8 @@
/**
* ANTLR parsing error listener.
*
* This listener will collect all errors, which may appear during a construction of parsed tree.
*
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class ParsingErrorListener extends BaseErrorListener {

View File

@ -20,7 +20,10 @@
import java.util.Collection;
/**
* Utility class that implements a helper method for achieving a delegation of parsed rules to multiple listeners.
*
* For example implementation of proxy parse tree listener see
* <a href="https://github.com/antlr/antlr4/issues/841">ANTLR issue</a> about it.
*/
public class ProxyParseTreeListenerUtil {
@ -28,6 +31,13 @@ private ProxyParseTreeListenerUtil() {
// instance of util class should never exists
}
/**
* Delegates enter rule event to collection of parsing listeners and captures parsing exceptions that may appear.
*
* @param ctx enter rule context; may not be null
* @param listeners collection of listeners; may not be null
* @param errors collection of errors; may not be null
*/
public static void delegateEnterRule(ParserRuleContext ctx, Collection<ParseTreeListener> listeners, Collection<ParsingException> errors) {
for (ParseTreeListener listener : listeners) {
try {
@ -40,6 +50,14 @@ public static void delegateEnterRule(ParserRuleContext ctx, Collection<ParseTree
}
}
/**
* Delegates exit rule event to collection of parsing listeners and captures parsing exceptions that may appear.
*
* @param ctx exit rule context; may not be null
* @param listeners collection of listeners; may not be null
* @param errors collection of errors; may not be null
*/
public static void delegateExitRule(ParserRuleContext ctx, Collection<ParseTreeListener> listeners, Collection<ParsingException> errors) {
for (ParseTreeListener listener : listeners) {
try {
@ -53,6 +71,14 @@ public static void delegateExitRule(ParserRuleContext ctx, Collection<ParseTreeL
}
/**
* Delegates visit error node event to collection of parsing listeners and captures parsing exceptions that may appear.
*
* @param node error node; may not be null
* @param listeners collection of listeners; may not be null
* @param errors collection of errors; may not be null
*/
public static void visitErrorNode(ErrorNode node, Collection<ParseTreeListener> listeners, Collection<ParsingException> errors) {
for (ParseTreeListener listener : listeners) {
try {
@ -64,6 +90,14 @@ public static void visitErrorNode(ErrorNode node, Collection<ParseTreeListener>
}
}
/**
* Delegates visit terminal event to collection of parsing listeners and captures parsing exceptions that may appear.
*
* @param node terminal node; may not be null
* @param listeners collection of listeners; may not be null
* @param errors collection of errors; may not be null
*/
public static void visitTerminal(TerminalNode node, Collection<ParseTreeListener> listeners, Collection<ParsingException> errors) {
for (ParseTreeListener listener : listeners) {
try {