/** * Utility to obtain a copy of a list of the columns that satisfy the specified predicate. * @param predicate the filter predicate; may not be null * @return the list of columns that satisfy the predicate; never null but possibly empty */ default List<Column> filterColumns( Predicate<Column> predicate ) { return columns().stream().filter(predicate).collect(Collectors.toList()); }
/** * Utility to obtain a copy of a list of the names of those columns that satisfy the specified predicate. * @param predicate the filter predicate; may not be null * @return the list of names of those columns that satisfy the predicate; never null but possibly empty */ default List<String> filterColumnNames( Predicate<Column> predicate ) { return columns().stream().filter(predicate).map(Column::name).collect(Collectors.toList()); }
private boolean hasMissingUntoastedColumns(Table table, List<ReplicationMessage.Column> columns) { List<String> msgColumnNames = columns.stream() .map(ReplicationMessage.Column::getName) .collect(Collectors.toList()); // Compute list of table columns not present in the replication message List<String> missingColumnNames = table.columns() .stream() .filter(c -> !msgColumnNames.contains(c.name())) .map(Column::name) .collect(Collectors.toList()); List<String> toastableColumns = schema().getToastableColumnsForTableId(table.id()); logger.debug("msg columns: '{}' --- missing columns: '{}' --- toastableColumns: '{}", String.join(",", msgColumnNames), String.join(",", missingColumnNames), String.join(",", toastableColumns)); // Return `true` if we have some columns not in the replication message that are not toastable or that we do // not recognize return !toastableColumns.containsAll(missingColumnNames); }
private void readTable(TableId tableId, ResultSet rs, BlockingConsumer<ChangeEvent> consumer, AtomicInteger rowsCounter) throws SQLException, InterruptedException { Table table = schema().tableFor(tableId); assert table != null; final int numColumns = table.columns().size(); final Object[] row = new Object[numColumns]; final ResultSetMetaData metaData = rs.getMetaData(); while (rs.next()) { rowsCounter.incrementAndGet(); sendCurrentRecord(consumer); for (int i = 0, j = 1; i != numColumns; ++i, ++j) { row[i] = valueForColumn(rs, j, metaData); } generateReadRecord(tableId, row); } }
protected TableImpl(Table table) { this(table.id(), table.columns(), table.primaryKeyColumnNames(), table.defaultCharsetName()); }
private Document toDocument(Table table) { Document document = Document.create(); document.set("defaultCharsetName", table.defaultCharsetName()); document.set("primaryKeyColumnNames", Array.create(table.primaryKeyColumnNames())); List<Document> columns = table.columns() .stream() .map(this::toDocument) .collect(Collectors.toList()); document.setArray("columns", Array.create(columns)); return document; }
private void addColumnFromTable(TableEditor table, String columnName, String newColumnName, Table selectedTable) { for (Column column : selectedTable.columns()) { if (column.name().equals(columnName)) { table.addColumn(column.edit().name(newColumnName).create()); break; } } } }
protected void assertValidPositions(Table editor) { AtomicInteger position = new AtomicInteger(1); assertThat(editor.columns().stream().allMatch(defn -> defn.position() == position.getAndIncrement())).isTrue(); } }
@Override public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof Table) { Table that = (Table) obj; return this.id().equals(that.id()) && this.columns().equals(that.columns()) && this.primaryKeyColumnNames().equals(that.primaryKeyColumnNames()) && Strings.equalsIgnoreCase(this.defaultCharsetName(), that.defaultCharsetName()); } return false; }
/** * Add or update the definition for the identified table. * * @param tableId the identifier of the table * @param changer the function that accepts the current {@link Table} and returns either the same or an updated * {@link Table}; may not be null * @return the previous table definition, or null if there was no prior table definition */ public Table updateTable(TableId tableId, Function<Table, Table> changer) { return lock.write(() -> { TableImpl existing = tablesByTableId.get(tableId); Table updated = changer.apply(existing); if (updated != existing) { tablesByTableId.put(tableId, new TableImpl(tableId, updated.columns(), updated.primaryKeyColumnNames(), updated.defaultCharsetName())); } changes.add(tableId); return existing; }); }
@Test public void shouldHaveColumns() { assertThat(table.retrieveColumnNames()).containsExactly("C1", "C2", "C3", "C4"); assertThat(table.columns()).containsExactly(c1, c2, c3, c4); }
@Test public void shouldUseFiltersForAlterTable() { parser = new MysqlDdlParserWithSimpleTestListener(listener, TableFilter.fromPredicate(x -> !x.table().contains("ignored"))); final String ddl = "CREATE TABLE ok (id int primary key, val smallint);" + System.lineSeparator() + "ALTER TABLE ignored ADD COLUMN(x tinyint)" + System.lineSeparator() + "ALTER TABLE ok ADD COLUMN(y tinyint)"; parser.parse(ddl, tables); assertThat(((MysqlDdlParserWithSimpleTestListener)parser).getParsingExceptionsFromWalker()).isEmpty(); assertThat(tables.size()).isEqualTo(1); final Table t1 = tables.forTable(null, null, "ok"); assertThat(t1.columns()).hasSize(3); final Column c1 = t1.columns().get(0); final Column c2 = t1.columns().get(1); final Column c3 = t1.columns().get(2); assertThat(c1.name()).isEqualTo("id"); assertThat(c1.typeName()).isEqualTo("INT"); assertThat(c2.name()).isEqualTo("val"); assertThat(c2.typeName()).isEqualTo("SMALLINT"); assertThat(c3.name()).isEqualTo("y"); assertThat(c3.typeName()).isEqualTo("TINYINT"); }
@Override public void exitCopyCreateTable(MySqlParser.CopyCreateTableContext ctx) { TableId tableId = parser.parseQualifiedTableId(ctx.tableName(0).fullId()); TableId originalTableId = parser.parseQualifiedTableId(ctx.tableName(1).fullId()); Table original = parser.databaseTables().forTable(originalTableId); if (original != null) { parser.databaseTables().overwriteTable(tableId, original.columns(), original.primaryKeyColumnNames(), original.defaultCharsetName()); parser.signalCreateTable(tableId, ctx); } super.exitCopyCreateTable(ctx); }
@Test @FixFor("DBZ-910") public void shouldParseConstraintCheck() { parser = new MysqlDdlParserWithSimpleTestListener(listener, true); final String ddl = "CREATE TABLE t1 (c1 INTEGER NOT NULL,c2 VARCHAR(22),CHECK (c2 IN ('A', 'B', 'C')));" + "CREATE TABLE t2 (c1 INTEGER NOT NULL,c2 VARCHAR(22),CONSTRAINT c1 CHECK (c2 IN ('A', 'B', 'C')));" + "CREATE TABLE t3 (c1 INTEGER NOT NULL,c2 VARCHAR(22),CONSTRAINT CHECK (c2 IN ('A', 'B', 'C')));" + "ALTER TABLE t1 ADD CONSTRAINT CHECK (c1 IN (1, 2, 3, 4));" + "ALTER TABLE t1 ADD CONSTRAINT c2 CHECK (c1 IN (1, 2, 3, 4))" + "ALTER TABLE t1 ADD CHECK (c1 IN (1, 2, 3, 4))"; parser.parse(ddl, tables); assertThat(tables.size()).isEqualTo(3); assertThat(tables.forTable(null, null, "t1").columns()).hasSize(2); assertThat(tables.forTable(null, null, "t2").columns()).hasSize(2); assertThat(tables.forTable(null, null, "t3").columns()).hasSize(2); }
@Test @FixFor("DBZ-780") public void shouldRenameColumnWithoutDefinition() { parser = new MysqlDdlParserWithSimpleTestListener(listener, TableFilter.fromPredicate(x -> !x.table().contains("ignored"))); final String ddl = "CREATE TABLE foo (id int primary key, old INT);" + System.lineSeparator() + "ALTER TABLE foo RENAME COLUMN old to new "; parser.parse(ddl, tables); assertThat(((MysqlDdlParserWithSimpleTestListener)parser).getParsingExceptionsFromWalker()).isEmpty(); assertThat(tables.size()).isEqualTo(1); final Table t1 = tables.forTable(null, null, "foo"); assertThat(t1.columns()).hasSize(2); final Column c1 = t1.columns().get(0); final Column c2 = t1.columns().get(1); assertThat(c1.name()).isEqualTo("id"); assertThat(c1.typeName()).isEqualTo("INT"); assertThat(c2.name()).isEqualTo("new"); assertThat(c2.typeName()).isEqualTo("INT"); }
@Test public void shouldCreateTableWhenEditorHasIdButNoColumns() { table = editor.tableId(id).create(); assertThat(table.columnWithName("any")).isNull(); assertThat(table.columns()).isEmpty(); assertThat(table.primaryKeyColumnNames()).isEmpty(); }
@Test @FixFor("DBZ-1059") public void shouldParseAlterTableRename() { final String ddl = "USE db;" + "CREATE TABLE db.t1 (ID INTEGER PRIMARY KEY);" + "ALTER TABLE `t1` RENAME TO `t2`;" + "ALTER TABLE `db`.`t2` RENAME TO `db`.`t3`;"; parser = new MysqlDdlParserWithSimpleTestListener(listener, true); parser.parse(ddl, tables); assertThat(tables.size()).isEqualTo(1); final Table table = tables.forTable(new TableId(null, "db", "t3")); assertThat(table).isNotNull(); assertThat(table.columns()).hasSize(1); }
@Test public void shouldFindGeneratedColumns() { editor.tableId(id); Column c1 = columnEditor.name("C1").type("VARCHAR").jdbcType(Types.VARCHAR).length(10).position(1).create(); Column c2 = columnEditor.name("C2").type("NUMBER").jdbcType(Types.NUMERIC).length(5).generated(true).create(); Column c3 = columnEditor.name("C3").type("DATE").jdbcType(Types.DATE).generated(true).create(); editor.addColumns(c1, c2, c3); editor.setPrimaryKeyNames("C1"); table = editor.create(); assertThat(table.retrieveColumnNames()).containsExactly("C1", "C2", "C3"); table.columns().forEach(col -> { assertThat(table.isGenerated(col.name())).isEqualTo(col.isGenerated()); }); assertValidPositions(editor); }
@Test public void shouldFindAutoIncrementedColumns() { editor.tableId(id); Column c1 = columnEditor.name("C1").type("VARCHAR").jdbcType(Types.VARCHAR).length(10).position(1).create(); Column c2 = columnEditor.name("C2").type("NUMBER").jdbcType(Types.NUMERIC).length(5).autoIncremented(true).create(); Column c3 = columnEditor.name("C3").type("DATE").jdbcType(Types.DATE).autoIncremented(true).create(); editor.addColumns(c1, c2, c3); editor.setPrimaryKeyNames("C1"); table = editor.create(); assertThat(table.retrieveColumnNames()).containsExactly("C1", "C2", "C3"); table.columns().forEach(col -> { assertThat(table.isAutoIncremented(col.name())).isEqualTo(col.isAutoIncremented()); }); assertValidPositions(editor); }
@Test public void shouldAllowAddingPrimaryKeyColumnWhenFound() { editor.tableId(id); Column c1 = columnEditor.name("C1").type("VARCHAR").jdbcType(Types.VARCHAR).length(10).position(1).create(); Column c2 = columnEditor.name("C2").type("NUMBER").jdbcType(Types.NUMERIC).length(5).position(1).create(); Column c3 = columnEditor.name("C3").type("DATE").jdbcType(Types.DATE).position(1).create(); editor.addColumns(c1, c2, c3); editor.setPrimaryKeyNames("C1"); c1 = editor.columnWithName(c1.name()); c2 = editor.columnWithName(c2.name()); c3 = editor.columnWithName(c3.name()); assertThat(c1.position()).isEqualTo(1); assertThat(c2.position()).isEqualTo(2); assertThat(c3.position()).isEqualTo(3); table = editor.create(); assertThat(table.retrieveColumnNames()).containsExactly("C1", "C2", "C3"); assertThat(table.columns()).containsExactly(c1, c2, c3); assertThat(table.primaryKeyColumnNames()).containsOnly("C1"); assertValidPositions(editor); }