public Table getTableSchemaFromChangeTable(ChangeTable changeTable) throws SQLException { final DatabaseMetaData metadata = connection().getMetaData(); final TableId changeTableId = changeTable.getChangeTableId(); List<ColumnEditor> columnEditors = new ArrayList<>(); try (ResultSet rs = metadata.getColumns(realDatabaseName, changeTableId.schema(), changeTableId.table(), null)) { while (rs.next()) { readTableColumn(rs, changeTableId, null).ifPresent(columnEditors::add); } } // The first 5 columns and the last column of the change table are CDC metadata final List<Column> columns = columnEditors.subList(CHANGE_TABLE_DATA_COLUMN_OFFSET, columnEditors.size() - 1).stream() .map(c -> c.position(c.position() - CHANGE_TABLE_DATA_COLUMN_OFFSET).create()) .collect(Collectors.toList()); final List<String> pkColumnNames = new ArrayList<>(); prepareQuery(GET_LIST_OF_KEY_COLUMNS, ps -> ps.setInt(1, changeTable.getChangeTableObjectId()), rs -> { while (rs.next()) { pkColumnNames.add(rs.getString(2)); } }); Collections.sort(columns); return Table.editor() .tableId(changeTable.getSourceTableId()) .addColumns(columns) .setPrimaryKeyNames(pkColumnNames) .create(); }
@Override public void enterUniqueKeyColumnConstraint(MySqlParser.UniqueKeyColumnConstraintContext ctx) { if (!tableEditor.hasPrimaryKey()) { // take the first unique constrain if no primary key is set tableEditor.addColumn(columnEditor.create()); tableEditor.setPrimaryKeyNames(columnEditor.name()); } super.enterUniqueKeyColumnConstraint(ctx); }
protected Column parseCreateColumn(Marker start, TableEditor table, String columnName, String newColumnName) { // Obtain the column editor ... Column existingColumn = table.columnWithName(columnName); ColumnEditor column = existingColumn != null ? existingColumn.edit() : Column.editor().name(columnName); AtomicBoolean isPrimaryKey = new AtomicBoolean(false); parseColumnDefinition(start, columnName, tokens, table, column, isPrimaryKey); convertDefaultValueToSchemaType(column); // Update the table ... Column newColumnDefn = column.create(); table.addColumns(newColumnDefn); if (isPrimaryKey.get()) { table.setPrimaryKeyNames(newColumnDefn.name()); } if (newColumnName != null && !newColumnName.equalsIgnoreCase(columnName)) { table.renameColumn(columnName, newColumnName); columnName = newColumnName; } // ALTER TABLE allows reordering the columns after the definition ... if (tokens.canConsume("FIRST")) { table.reorderColumn(columnName, null); } else if (tokens.canConsume("AFTER")) { table.reorderColumn(columnName, tokens.consume()); } return table.columnWithName(newColumnDefn.name()); }
@Override public TableEditor edit() { return new TableEditorImpl().tableId(id) .setColumns(columnDefs) .setPrimaryKeyNames(pkColumnNames) .setDefaultCharsetName(defaultCharsetName); } }
@Override public void exitColumnCreateTable(MySqlParser.ColumnCreateTableContext ctx) { parser.runIfNotNull(() -> { // Make sure that the table's character set has been set ... if (!tableEditor.hasDefaultCharsetName()) { tableEditor.setDefaultCharsetName(parser.currentDatabaseCharset()); } listeners.remove(columnDefinitionListener); columnDefinitionListener = null; // remove column definition parser listener parser.databaseTables().overwriteTable(tableEditor.create()); parser.signalCreateTable(tableEditor.tableId(), ctx); }, tableEditor); super.exitColumnCreateTable(ctx); }
@Test public void shouldReorderColumns() { 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); assertValidPositions(editor); editor.reorderColumn("C1", null); assertThat(editor.columns()).containsExactly(editor.columnWithName("C1"), editor.columnWithName("C2"), editor.columnWithName("C3")); assertValidPositions(editor); editor.reorderColumn("C2", "C1"); assertThat(editor.columns()).containsExactly(editor.columnWithName("C1"), editor.columnWithName("C2"), editor.columnWithName("C3")); assertValidPositions(editor); editor.reorderColumn("C3", "C2"); assertThat(editor.columns()).containsExactly(editor.columnWithName("C1"), editor.columnWithName("C2"), editor.columnWithName("C3")); assertValidPositions(editor); editor.reorderColumn("C3", "C1"); assertThat(editor.columns()).containsExactly(editor.columnWithName("C1"), editor.columnWithName("C3"), editor.columnWithName("C2")); assertValidPositions(editor); editor.reorderColumn("C3", null); assertThat(editor.columns()).containsExactly(editor.columnWithName("C3"),
table.addColumn(newColumnDefn); if (isPrimaryKey.get()) { table.setPrimaryKeyNames(newColumnDefn.name()); Column existingColumn = table.columnWithName(columnName); ColumnEditor column = existingColumn != null ? existingColumn.edit() : Column.editor().name(columnName); parseAlterColumn(start, column); table.setColumns(newColumnDefn); } else if (tokens.matches("DROP", "CONSTRAINT")) { parseDropTableConstraint(start, table); databaseTables.overwriteTable(table.create());
private static Table fromDocument(TableId id, Document document) { TableEditor editor = Table.editor() .tableId(id) .setDefaultCharsetName(document.getString("defaultCharsetName")); .forEach(editor::addColumn); editor.setPrimaryKeyNames(document.getArray("primaryKeyColumnNames") .streamValues() .map(Value::asString) .collect(Collectors.toList())); return editor.create();
@Override public void exitSubqueryTableItem(MySqlParser.SubqueryTableItemContext ctx) { parser.runIfNotNull(() -> { // parsing subselect String tableAlias = parser.parseName(ctx.uid()); TableId aliasTableId = parser.resolveTableId(parser.currentSchema(), tableAlias); selectTableEditor.tableId(aliasTableId); tableByAlias.put(aliasTableId, selectTableEditor.create()); }, tableEditor); super.exitSubqueryTableItem(ctx); }
.tableId(new TableId(databaseName, schemaName, "foo")) .addColumn(Column.editor() .name("first") .jdbcType(Types.VARCHAR) .optional(false) .create()) .setPrimaryKeyNames("first") .create();
table.addColumn(Column.editor().name(name).create()); }); if (table.columns().isEmpty()) { selectedColumnsByAlias.forEach((columnName, fromTableColumn) -> { if (fromTableColumn != null && columnName != null) table.addColumn(fromTableColumn.edit().name(columnName).create()); }); } else { List<Column> changedColumns = new ArrayList<>(); table.columns().forEach(column -> { table.setPrimaryKeyNames(viewPkColumnNames); databaseTables.overwriteTable(table.create());
editor.tableId(tableIdWithCatalog); List<String> columnNames = new ArrayList<>(editor.columnNames()); for (String columnName : columnNames) { Column column = editor.columnWithName(columnName); if (column.jdbcType() == Types.TIMESTAMP) { editor.addColumn( column.edit() .length(column.scale().orElse(Column.UNSET_INT_VALUE)) .filter(s -> s == ORACLE_UNSET_SCALE) .ifPresent(s -> { editor.addColumn( column.edit() .scale(null) tables.overwriteTable(editor.create());
private Table tableFromFromMessage(List<ReplicationMessage.Column> columns, Table table) { return table.edit() .setColumns(columns.stream() .map(column -> { final PostgresType type = column.getType(); final ColumnEditor columnEditor = Column.editor() .name(column.getName()) .jdbcType(type.getJdbcId()) .type(type.getName()) .optional(column.isOptional()) .nativeType(type.getOid()); columnEditor.length(column.getTypeMetadata().getLength()); columnEditor.scale(column.getTypeMetadata().getScale()); return columnEditor.create(); }) .collect(Collectors.toList()) ) .setPrimaryKeyNames(table.filterColumnNames(c -> table.isPrimaryKeyColumn(c.name()))).create(); } }
protected void parseTableElement(Marker start, TableEditor table) { if (tokens.matchesAnyOf("CONSTRAINT", "UNIQUE", "PRIMARY", "FOREIGN", "CHECK")) { parseTableConstraintDefinition(start, table); } else if (tokens.matches("LIKE")) { parseTableLikeClause(start, table); } else if (tokens.matches("REF", "IS")) { parseSelfReferencingColumnSpec(start, table); } else { // Obtain the column editor ... String columnName = tokens.consume(); Column existingColumn = table.columnWithName(columnName); ColumnEditor column = existingColumn != null ? existingColumn.edit() : Column.editor().name(columnName); AtomicBoolean isPrimaryKey = new AtomicBoolean(false); if (tokens.matches("WITH", "OPTIONS")) { parseColumnOptions(start, columnName, tokens, column); } else { parseColumnDefinition(start, columnName, tokens, table, column, isPrimaryKey); } // Update the table ... Column newColumnDefn = column.create(); table.addColumns(newColumnDefn); if (isPrimaryKey.get()) { table.setPrimaryKeyNames(newColumnDefn.name()); } } }
protected void parseCreateIndex(Marker start) { boolean unique = tokens.canConsume("UNIQUE"); tokens.canConsumeAnyOf("FULLTEXT", "SPATIAL"); tokens.consume("INDEX"); String indexName = tokens.consume(); // index name if (tokens.matches("USING")) { parseIndexType(start); } TableId tableId = null; if (tokens.canConsume("ON")) { // Usually this is required, but in some cases ON is not required tableId = parseQualifiedTableName(start); } if (unique && tableId != null) { // This is a unique index, and we can mark the index's columns as the primary key iff there is not already // a primary key on the table. (Should a PK be created later via an alter, then it will overwrite this.) TableEditor table = databaseTables.editTable(tableId); if (table != null && !table.hasPrimaryKey()) { List<String> names = parseIndexColumnNames(start); if (table.columns().stream().allMatch(Column::isRequired)) { databaseTables.overwriteTable(table.setPrimaryKeyNames(names).create()); } } } // We don't care about any other statements or the rest of this statement ... consumeRemainingStatement(start); // TODO fix: signal should be send only when some changes on table are made signalCreateIndex(indexName, tableId, start); debugParsed(start); }
@Override public void exitAlterView(MySqlParser.AlterViewContext ctx) { parser.runIfNotNull(() -> { tableEditor.addColumns(selectColumnsListener.getSelectedColumns()); // Make sure that the table's character set has been set ... if (!tableEditor.hasDefaultCharsetName()) { tableEditor.setDefaultCharsetName(parser.currentDatabaseCharset()); } parser.databaseTables().overwriteTable(tableEditor.create()); listeners.remove(selectColumnsListener); }, tableEditor); // signal view even if it was skipped parser.signalAlterView(parser.parseQualifiedTableId(ctx.fullId()), null, ctx); super.exitAlterView(ctx); } }
table.setPrimaryKeyNames(pkColumnNames); parseIndexOptions(start); Column c = table.columnWithName(name); if (c != null && c.isOptional()) { final ColumnEditor ce = c.edit().optional(false); ce.unsetDefaultValue(); table.addColumn(ce.create()); if (table.primaryKeyColumnNames().isEmpty()) { table.setPrimaryKeyNames(uniqueKeyColumnNames); // this may eventually get overwritten by a real PK
columnName = withoutQuotes(indexColumnNameContext.STRING_LITERAL().getText()); Column column = tableEditor.columnWithName(columnName); if (column != null && column.isOptional()) { final ColumnEditor ce = column.edit().optional(false); ce.unsetDefaultValue(); tableEditor.addColumn(ce.create()); .collect(Collectors.toList()); tableEditor.setPrimaryKeyNames(pkColumnNames);
@Test(expected = IllegalArgumentException.class) public void shouldNotAllowAddingPrimaryKeyColumnWhenNotFound() { 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", "WOOPS"); }
@Override public void enterAlterByModifyColumn(MySqlParser.AlterByModifyColumnContext ctx) { parser.runIfNotNull(() -> { String columnName = parser.parseName(ctx.uid(0)); Column column = tableEditor.columnWithName(columnName); if (column != null) { columnDefinitionListener = new ColumnDefinitionParserListener(tableEditor, column.edit(), parser.dataTypeResolver(), parser.getConverters()); listeners.add(columnDefinitionListener); } else { throw new ParsingException(null, "Trying to change column " + columnName + " in " + tableEditor.tableId().toString() + " table, which does not exist. Query: " + getText(ctx)); } }, tableEditor); super.enterAlterByModifyColumn(ctx); }