DBZ-252 add parser listeners for alter view and drop view statements + test for it

This commit is contained in:
rkuchar 2018-05-02 13:08:08 +02:00 committed by Gunnar Morling
parent 7d7f740721
commit c3b65ac2c3
7 changed files with 230 additions and 9 deletions

View File

@ -37,7 +37,11 @@ public abstract class AntlrDdlParser<L extends Lexer, P extends Parser> extends
protected DataTypeResolver dataTypeResolver = new DataTypeResolver();
public AntlrDdlParser(boolean throwErrorsFromTreeWalk) {
super(";");
this(throwErrorsFromTreeWalk, false);
}
public AntlrDdlParser(boolean throwErrorsFromTreeWalk, boolean includeViews) {
super(";", includeViews);
this.throwErrorsFromTreeWalk = throwErrorsFromTreeWalk;
}
@ -196,8 +200,8 @@ public void signalAlterTable(TableId id, TableId previousId, ParserRuleContext c
}
@Override
public void signalDropTable(TableId id, String ctx) {
super.signalDropTable(id, ctx);
public void signalDropTable(TableId id, String statement) {
super.signalDropTable(id, statement);
}
/**

View File

@ -43,7 +43,11 @@ public MySqlAntlrDdlParser() {
}
public MySqlAntlrDdlParser(boolean throwErrorsFromTreeWalk) {
super(throwErrorsFromTreeWalk);
this(throwErrorsFromTreeWalk, false);
}
public MySqlAntlrDdlParser(boolean throwErrorsFromTreeWalk, boolean includeViews) {
super(throwErrorsFromTreeWalk, includeViews);
systemVariables = new MySqlSystemVariables();
}

View File

@ -0,0 +1,77 @@
/*
* 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.antlr.mysql.listener;
import io.debezium.antlr.AntlrDdlParser;
import io.debezium.antlr.mysql.MySqlAntlrDdlParser;
import io.debezium.ddl.parser.mysql.generated.MySqlParser;
import io.debezium.ddl.parser.mysql.generated.MySqlParserBaseListener;
import io.debezium.relational.Column;
import io.debezium.relational.TableEditor;
import io.debezium.relational.TableId;
import io.debezium.text.ParsingException;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import java.util.List;
/**
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class AlterViewParserListener extends MySqlParserBaseListener {
private final MySqlAntlrDdlParser parserCtx;
private final List<ParseTreeListener> listeners;
private TableEditor tableEditor;
private ViewSelectedColumnsParserListener selectColumnsListener;
public AlterViewParserListener(MySqlAntlrDdlParser parserCtx, List<ParseTreeListener> listeners) {
this.parserCtx = parserCtx;
this.listeners = listeners;
}
@Override
public void enterAlterView(MySqlParser.AlterViewContext ctx) {
if (!parserCtx.skipViews()) {
TableId tableId = parserCtx.parseQualifiedTableId(ctx.fullId());
tableEditor = parserCtx.databaseTables().editTable(tableId);
if (tableEditor == null) {
throw new ParsingException(null, "Trying to alter view " + parserCtx.getFullTableName(tableId)
+ ", which does not exists. Query:" + AntlrDdlParser.getText(ctx));
}
// alter view will override existing columns for a new one
tableEditor.columnNames().forEach(tableEditor::removeColumn);
// create new columns just with specified name for now
if (ctx.uidList() != null) {
ctx.uidList().uid().stream().map(parserCtx::parseName).forEach(columnName -> {
tableEditor.addColumn(Column.editor().name(columnName).create());
});
}
selectColumnsListener = new ViewSelectedColumnsParserListener(tableEditor, parserCtx);
listeners.add(selectColumnsListener);
}
super.enterAlterView(ctx);
}
@Override
public void exitAlterView(MySqlParser.AlterViewContext ctx) {
parserCtx.runIfNotNull(() -> {
tableEditor.addColumns(selectColumnsListener.getSelectedColumns());
// Make sure that the table's character set has been set ...
if (!tableEditor.hasDefaultCharsetName()) {
tableEditor.setDefaultCharsetName(parserCtx.currentDatabaseCharset());
}
parserCtx.databaseTables().overwriteTable(tableEditor.create());
listeners.remove(selectColumnsListener);
}, tableEditor);
// signal view even if it was skipped
parserCtx.signalAlterView(parserCtx.parseQualifiedTableId(ctx.fullId()), null, ctx);
super.exitAlterView(ctx);
}
}

View File

@ -37,8 +37,8 @@ public void enterCreateView(MySqlParser.CreateViewContext ctx) {
tableEditor = parserCtx.databaseTables().editOrCreateTable(parserCtx.parseQualifiedTableId(ctx.fullId()));
// create new columns just with specified name for now
if (ctx.uidList() != null) {
ctx.uidList().uid().forEach(uidContext -> {
tableEditor.addColumn(Column.editor().name(parserCtx.parseName(uidContext)).create());
ctx.uidList().uid().stream().map(parserCtx::parseName).forEach(columnName -> {
tableEditor.addColumn(Column.editor().name(columnName).create());
});
}
selectColumnsListener = new ViewSelectedColumnsParserListener(tableEditor, parserCtx);
@ -58,7 +58,7 @@ public void exitCreateView(MySqlParser.CreateViewContext ctx) {
parserCtx.databaseTables().overwriteTable(tableEditor.create());
listeners.remove(selectColumnsListener);
}, tableEditor);
// TODO rkuchar move into lambda function
// signal view even if it was skipped
parserCtx.signalCreateView(parserCtx.parseQualifiedTableId(ctx.fullId()), ctx);
super.exitCreateView(ctx);
}

View File

@ -0,0 +1,32 @@
/*
* 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.antlr.mysql.listener;
import io.debezium.antlr.mysql.MySqlAntlrDdlParser;
import io.debezium.ddl.parser.mysql.generated.MySqlParser;
import io.debezium.ddl.parser.mysql.generated.MySqlParserBaseListener;
/**
* @author Roman Kuchár <kucharrom@gmail.com>.
*/
public class DropViewParserListener extends MySqlParserBaseListener {
private final MySqlAntlrDdlParser parserCtx;
public DropViewParserListener(MySqlAntlrDdlParser parserCtx) {
this.parserCtx = parserCtx;
}
@Override
public void enterDropView(MySqlParser.DropViewContext ctx) {
ctx.fullId().stream().map(parserCtx::parseQualifiedTableId).forEach(tableId -> {
parserCtx.databaseTables().removeTable(tableId);
parserCtx.signalDropView(tableId, ctx);
});
super.enterDropView(ctx);
}
}

View File

@ -49,6 +49,8 @@ public MySqlAntlrDdlParserListener(MySqlAntlrDdlParser parserCtx) {
listeners.add(new RenameTableParserListener(parserCtx));
listeners.add(new TruncateTableParserListener(parserCtx));
listeners.add(new CreateViewParserListener(parserCtx, listeners));
listeners.add(new AlterViewParserListener(parserCtx, listeners));
listeners.add(new DropViewParserListener(parserCtx));
listeners.add(new CreateUniqueIndexParserListener(parserCtx));
listeners.add(new SetStatementParserListener(parserCtx));
listeners.add(new UseStatementParserListener(parserCtx));

View File

@ -107,6 +107,102 @@ public void shouldParseCreateTableStatementWithSingleGeneratedAndPrimaryKeyColum
assertColumn(foo, "c2", "VARCHAR", Types.VARCHAR, 22, -1, true, false, false);
}
@Test
public void shouldParseCreateViewStatementStartSelect() {
String ddl = "CREATE TABLE foo ( " + System.lineSeparator()
+ " c1 INTEGER NOT NULL AUTO_INCREMENT, " + System.lineSeparator()
+ " c2 VARCHAR(22) " + System.lineSeparator()
+ "); " + System.lineSeparator();
String ddl2 = "CREATE VIEW fooView AS (SELECT * FROM foo)" + System.lineSeparator();
parser = new MysqlDdlParserWithSimpleTestListener(listener, true);
parser.parse(ddl, tables);
parser.parse(ddl2, tables);
assertThat(tables.size()).isEqualTo(2);
Table foo = tables.forTable(new TableId(null, null, "fooView"));
assertThat(foo).isNotNull();
assertThat(foo.columnNames()).containsExactly("c1", "c2");
assertThat(foo.primaryKeyColumnNames()).isEmpty();
assertColumn(foo, "c1", "INTEGER", Types.INTEGER, -1, -1, false, true, true);
assertColumn(foo, "c2", "VARCHAR", Types.VARCHAR, 22, -1, true, false, false);
}
@Test
public void shouldParseDropView() {
String ddl = "CREATE TABLE foo ( " + System.lineSeparator()
+ " c1 INTEGER NOT NULL AUTO_INCREMENT, " + System.lineSeparator()
+ " c2 VARCHAR(22) " + System.lineSeparator()
+ "); " + System.lineSeparator();
String ddl2 = "CREATE VIEW fooView AS (SELECT * FROM foo)" + System.lineSeparator();
String ddl3 = "DROP VIEW fooView";
parser = new MysqlDdlParserWithSimpleTestListener(listener, true);
parser.parse(ddl, tables);
parser.parse(ddl2, tables);
parser.parse(ddl3, tables);
assertThat(tables.size()).isEqualTo(1);
Table foo = tables.forTable(new TableId(null, null, "fooView"));
assertThat(foo).isNull();
}
@Test
public void shouldParseCreateViewStatementColumnAlias() {
String ddl = "CREATE TABLE foo ( " + System.lineSeparator()
+ " c1 INTEGER NOT NULL AUTO_INCREMENT, " + System.lineSeparator()
+ " c2 VARCHAR(22) " + System.lineSeparator()
+ "); " + System.lineSeparator();
String ddl2 = "CREATE VIEW fooView(w1) AS (SELECT c2 as w1 FROM foo)" + System.lineSeparator();
parser = new MysqlDdlParserWithSimpleTestListener(listener, true);
parser.parse(ddl, tables);
parser.parse(ddl2, tables);
assertThat(tables.size()).isEqualTo(2);
Table foo = tables.forTable(new TableId(null, null, "fooView"));
assertThat(foo).isNotNull();
assertThat(foo.columnNames()).containsExactly("w1");
assertThat(foo.primaryKeyColumnNames()).isEmpty();
assertColumn(foo, "w1", "VARCHAR", Types.VARCHAR, 22, -1, true, false, false);
}
@Test
public void shouldParseCreateViewStatementColumnAliasInnerSelect() {
String ddl = "CREATE TABLE foo ( " + System.lineSeparator()
+ " c1 INTEGER NOT NULL AUTO_INCREMENT, " + System.lineSeparator()
+ " c2 VARCHAR(22) " + System.lineSeparator()
+ "); " + System.lineSeparator();
String ddl2 = "CREATE VIEW fooView(w1) AS (SELECT foo2.c2 as w1 FROM (SELECT c1 as c2 FROM foo) AS foo2)" + System.lineSeparator();
parser = new MysqlDdlParserWithSimpleTestListener(listener, true);
parser.parse(ddl, tables);
parser.parse(ddl2, tables);
assertThat(tables.size()).isEqualTo(2);
Table foo = tables.forTable(new TableId(null, null, "fooView"));
assertThat(foo).isNotNull();
assertThat(foo.columnNames()).containsExactly("w1");
assertThat(foo.primaryKeyColumnNames()).isEmpty();
assertColumn(foo, "w1", "INTEGER", Types.INTEGER, -1, -1, false, true, true);
}
@Test
public void shouldParseAlterViewStatementColumnAliasInnerSelect() {
String ddl = "CREATE TABLE foo ( " + System.lineSeparator()
+ " c1 INTEGER NOT NULL AUTO_INCREMENT, " + System.lineSeparator()
+ " c2 VARCHAR(22) " + System.lineSeparator()
+ "); " + System.lineSeparator();
String ddl2 = "CREATE VIEW fooView(w1) AS (SELECT foo2.c2 as w1 FROM (SELECT c1 as c2 FROM foo) AS foo2)" + System.lineSeparator();
String ddl3 = "ALTER VIEW fooView AS (SELECT c2 FROM foo)";
parser = new MysqlDdlParserWithSimpleTestListener(listener, true);
parser.parse(ddl, tables);
parser.parse(ddl2, tables);
parser.parse(ddl3, tables);
assertThat(tables.size()).isEqualTo(2);
assertThat(listener.total()).isEqualTo(3);
Table foo = tables.forTable(new TableId(null, null, "fooView"));
assertThat(foo).isNotNull();
assertThat(foo.columnNames()).containsExactly("c2");
assertThat(foo.primaryKeyColumnNames()).isEmpty();
assertColumn(foo, "c2", "VARCHAR", Types.VARCHAR, 22, -1, true, false, false);
}
@Test
public void shouldParseCreateTableStatementWithSingleGeneratedColumnAsPrimaryKey() {
String ddl = "CREATE TABLE my.foo ( " + System.lineSeparator()
@ -755,7 +851,9 @@ public void shouldParseTestStatements() {
// legacy parser was signaling all created index
// antlr is parsing only those, which will make any model changes
int numberOfNonUniqueIndexesCreated = 2;
assertThat(listener.total()).isEqualTo(58 - numberOfAlteredTablesWhichDoesNotExists - numberOfNonUniqueIndexesCreated);
int numberOfAlterViewStatements = 6;
assertThat(listener.total()).isEqualTo(58 - numberOfAlteredTablesWhichDoesNotExists
- numberOfNonUniqueIndexesCreated + numberOfAlterViewStatements);
listener.forEach(this::printEvent);
}
@ -1563,7 +1661,11 @@ protected void assertColumn(Table table, String name, String typeName, int jdbcT
class MysqlDdlParserWithSimpleTestListener extends MySqlAntlrDdlParser {
public MysqlDdlParserWithSimpleTestListener(DdlChanges changesListener) {
super(false);
this(changesListener, false);
}
public MysqlDdlParserWithSimpleTestListener(DdlChanges changesListener, boolean includeViews) {
super(false, includeViews);
this.ddlChanges = changesListener;
}
}