All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.wz2cool.canal.utils.converter.BaseAlterSqlConverter Maven / Gradle / Ivy

The newest version!
package com.github.wz2cool.canal.utils.converter;

import com.github.wz2cool.canal.utils.model.AlterColumnExpression;
import com.github.wz2cool.canal.utils.model.EnhancedAlterOperation;
import com.github.wz2cool.canal.utils.model.exception.NotSupportDataTypeException;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.alter.Alter;
import net.sf.jsqlparser.statement.alter.AlterExpression;
import net.sf.jsqlparser.statement.alter.AlterOperation;
import net.sf.jsqlparser.statement.create.table.ColDataType;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author Frank
 */
public abstract class BaseAlterSqlConverter {
    public List convert(String mysqlAlterSqlInput) throws JSQLParserException {
        String mysqlAlterSql = cleanMysqlAlterSql(mysqlAlterSqlInput);

        List result = new ArrayList<>();
        net.sf.jsqlparser.statement.Statement statement = CCJSqlParserUtil.parse(mysqlAlterSql);
        if (!(statement instanceof Alter)) {
            return result;
        }

        Alter mysqlAlter = (Alter) statement;
        List alterColumnExpressions = getAlterColumnExpressions(mysqlAlter);
        for (AlterColumnExpression alterColumnExpression : alterColumnExpressions) {
            alterColumnExpression.setSchemaName(mysqlAlter.getTable().getSchemaName());
        }
        List addColumnSqlList = alterColumnExpressions
                .stream()
                .filter(x -> x.getOperation() == EnhancedAlterOperation.ADD_COLUMN)
                .map(this::convertToAddColumnSql)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList());
        List changeColumnTypeSqlList = alterColumnExpressions
                .stream()
                .filter(x -> x.getOperation() == EnhancedAlterOperation.CHANGE_COLUMN_TYPE)
                .map(this::convertToChangeColumnTypeSql)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList());
        List renameColumnSqlList = alterColumnExpressions
                .stream()
                .filter(x -> x.getOperation() == EnhancedAlterOperation.RENAME_COLUMN)
                .map(this::convertToRenameColumnSql)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList());
        List dropColumnSqlList = alterColumnExpressions
                .stream()
                .filter(x -> x.getOperation() == EnhancedAlterOperation.DROP_COLUMN)
                .map(this::convertToDropColumnSql)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList());

        List otherColumnActionSqlList = convertToOtherColumnActionSqlList(alterColumnExpressions);

        result.addAll(addColumnSqlList);
        result.addAll(changeColumnTypeSqlList);
        result.addAll(renameColumnSqlList);
        result.addAll(dropColumnSqlList);
        result.addAll(otherColumnActionSqlList);
        return result;
    }

    protected abstract IColDataTypeConverter getColDataTypeConverter();

    protected abstract Optional convertToAddColumnSql(AlterColumnExpression alterColumnExpression);

    protected abstract Optional convertToChangeColumnTypeSql(AlterColumnExpression alterColumnExpression);

    protected abstract Optional convertToRenameColumnSql(AlterColumnExpression alterColumnExpression);

    protected abstract Optional convertToDropColumnSql(AlterColumnExpression alterColumnExpression);

    protected abstract List convertToOtherColumnActionSqlList(List alterColumnExpressions);

    private List getAlterColumnExpressions(Alter mysqlAlter) {
        List result = new ArrayList<>();
        if (mysqlAlter == null) {
            return result;
        }

        List alterExpressions = mysqlAlter.getAlterExpressions();
        List alterExpressionsForColumn = new ArrayList<>();
        String tableName = cleanText(mysqlAlter.getTable().getName());
        for (AlterExpression alterExpression : alterExpressions) {
            String optionalSpecifier = alterExpression.getOptionalSpecifier();
            if ("COLUMN".equalsIgnoreCase(optionalSpecifier)
                    || (alterExpression.getColDataTypeList() != null && !alterExpression.getColDataTypeList().isEmpty())
                    || (alterExpression.getOperation() == AlterOperation.DROP && alterExpression.getColumnName() != null)) {
                alterExpressionsForColumn.add(alterExpression);
            }
        }

        if (alterExpressionsForColumn.isEmpty()) {
            return result;
        }

        for (AlterExpression alterExpression : alterExpressionsForColumn) {
            List alterColumnExpressions =
                    getAlterColumnExpressions(tableName, alterExpression);
            result.addAll(alterColumnExpressions);
        }

        return result;
    }


    protected String getDataTypeString(final ColDataType colDataType) {
        List argumentsStringList = colDataType.getArgumentsStringList();
        if (argumentsStringList == null || argumentsStringList.isEmpty()) {
            return colDataType.getDataType();
        } else {
            return colDataType.toString();
        }
    }

    private List getAlterColumnExpressions(
            final String tableName, final AlterExpression alterExpression) {
        List result = new ArrayList<>();
        if (alterExpression == null) {
            return result;
        }

        AlterOperation operation = alterExpression.getOperation();
        String columnName = alterExpression.getColumnName();
        String colOldName = alterExpression.getColumnOldName();
        List colDataTypeList = alterExpression.getColDataTypeList();

        Optional dropColumnExpression =
                getDropColumnExpression(operation, tableName, columnName);
        dropColumnExpression.ifPresent(result::add);

        List renameColumnExpressions =
                getRenameColumnExpressions(operation, tableName, colOldName, colDataTypeList);
        result.addAll(renameColumnExpressions);

        List addColumnExpressions =
                getAddColumnExpressions(operation, tableName, colDataTypeList);
        result.addAll(addColumnExpressions);

        List changeColumnTypeExpressions =
                getChangeColumnTypeExpressions(operation, tableName, colOldName, colDataTypeList);
        result.addAll(changeColumnTypeExpressions);
        return result;
    }

    private List getAddColumnExpressions(
            final AlterOperation alterOperation,
            final String tableName,
            final List columnDataTypes) {
        List result = new ArrayList<>();
        if (alterOperation != AlterOperation.ADD || columnDataTypes == null || columnDataTypes.isEmpty()) {
            return result;
        }

        for (AlterExpression.ColumnDataType columnDataType : columnDataTypes) {
            AlterColumnExpression addColumnExpression = new AlterColumnExpression();
            String columnName = columnDataType.getColumnName();

            ColDataType mysqlColDataType = columnDataType.getColDataType();
            Optional covColDataTypeOptional = getColDataTypeConverter().convert(mysqlColDataType);
            if (!covColDataTypeOptional.isPresent()) {
                String errorMsg = String.format("[Add Column] Cannot convert data type: %s",
                        mysqlColDataType.getDataType());
                throw new NotSupportDataTypeException(errorMsg);
            }

            final boolean unsignedFlag = columnDataType.getColumnSpecs().stream().anyMatch("unsigned"::equalsIgnoreCase);
            addColumnExpression.setTableName(cleanText(tableName));
            addColumnExpression.setColumnName(cleanText(columnName));
            addColumnExpression.setOperation(EnhancedAlterOperation.ADD_COLUMN);
            addColumnExpression.setColDataType(covColDataTypeOptional.get());
            addColumnExpression.setUnsignedFlag(unsignedFlag);
            // 说明
            List columnSpecs = columnDataType.getColumnSpecs();
            addColumnExpression = setColumnSpecs(addColumnExpression, columnSpecs);
            result.add(addColumnExpression);
        }
        return result;
    }

    private List getRenameColumnExpressions(
            final AlterOperation alterOperation,
            final String tableName,
            final String colOldName,
            final List columnDataTypes
    ) {
        List result = new ArrayList<>();
        if (alterOperation != AlterOperation.CHANGE
                || colOldName == null
                || columnDataTypes == null
                || columnDataTypes.isEmpty()) {
            return result;
        }

        for (AlterExpression.ColumnDataType columnDataType : columnDataTypes) {
            String columnName = columnDataType.getColumnName();
            if (columnName.equals(colOldName)) {
                // ignore if column name not change.
                continue;
            }

            ColDataType mysqlColDataType = columnDataType.getColDataType();
            Optional covColDataTypeOptional = getColDataTypeConverter().convert(mysqlColDataType);
            if (!covColDataTypeOptional.isPresent()) {
                String errorMsg = String.format("[Rename Column] Cannot convert data type: %s",
                        mysqlColDataType.getDataType());
                throw new NotSupportDataTypeException(errorMsg);
            }
            final boolean unsignedFlag = columnDataType.getColumnSpecs().stream().anyMatch("unsigned"::equalsIgnoreCase);
            AlterColumnExpression renameColumnExpression = new AlterColumnExpression();
            renameColumnExpression.setTableName(cleanText(tableName));
            renameColumnExpression.setColumnName(cleanText(columnName));
            renameColumnExpression.setColOldName(cleanText(colOldName));
            renameColumnExpression.setOperation(EnhancedAlterOperation.RENAME_COLUMN);
            renameColumnExpression.setColDataType(covColDataTypeOptional.get());
            renameColumnExpression.setUnsignedFlag(unsignedFlag);
            // 说明
            List columnSpecs = columnDataType.getColumnSpecs();
            renameColumnExpression = setColumnSpecs(renameColumnExpression, columnSpecs);
            result.add(renameColumnExpression);
        }
        return result;
    }

    private List getChangeColumnTypeExpressions(
            final AlterOperation alterOperation,
            final String tableName,
            final String colOldName,
            final List columnDataTypes) {
        List result = new ArrayList<>();
        // https://stackoverflow.com/questions/14767174/modify-column-vs-change-column
        // MODIFY COLUMN This command does everything CHANGE COLUMN can, but without renaming the column.
        if (!(alterOperation == AlterOperation.CHANGE || alterOperation == AlterOperation.MODIFY)
                || columnDataTypes == null
                || columnDataTypes.isEmpty()) {
            return result;
        }

        for (AlterExpression.ColumnDataType columnDataType : columnDataTypes) {
            AlterColumnExpression changeTypeColumnExpression = new AlterColumnExpression();
            String columnName = columnDataType.getColumnName();
            if (alterOperation == AlterOperation.CHANGE && !columnName.equals(colOldName)) {
                // need check rename
                continue;
            }

            ColDataType mysqlColDataType = columnDataType.getColDataType();
            Optional covColDataTypeOptional = getColDataTypeConverter().convert(mysqlColDataType);
            if (!covColDataTypeOptional.isPresent()) {
                String errorMsg = String.format("[Change Column Type] Cannot convert data type: %s",
                        mysqlColDataType.getDataType());
                throw new NotSupportDataTypeException(errorMsg);
            }
            final boolean unsignedFlag = columnDataType.getColumnSpecs().stream().anyMatch("unsigned"::equalsIgnoreCase);
            changeTypeColumnExpression.setTableName(cleanText(tableName));
            changeTypeColumnExpression.setColumnName(cleanText(columnName));
            changeTypeColumnExpression.setOperation(EnhancedAlterOperation.CHANGE_COLUMN_TYPE);
            changeTypeColumnExpression.setColDataType(covColDataTypeOptional.get());
            changeTypeColumnExpression.setUnsignedFlag(unsignedFlag);
            List columnSpecs = columnDataType.getColumnSpecs();
            changeTypeColumnExpression = setColumnSpecs(changeTypeColumnExpression, columnSpecs);
            result.add(changeTypeColumnExpression);
        }
        return result;
    }

    private AlterColumnExpression setColumnSpecs(AlterColumnExpression columnExpression, List columnSpecs) {

        AlterColumnExpression useColumnExpression = new AlterColumnExpression();
        useColumnExpression.setOperation(columnExpression.getOperation());
        useColumnExpression.setTableName(columnExpression.getTableName());
        useColumnExpression.setColumnName(columnExpression.getColumnName());
        useColumnExpression.setColOldName(columnExpression.getColOldName());
        useColumnExpression.setColDataType(columnExpression.getColDataType());
        useColumnExpression.setUnsignedFlag(columnExpression.isUnsignedFlag());
        useColumnExpression.setCommentText("");
        useColumnExpression.setNullAble("NULL");
        useColumnExpression.setDefaultValue("");
        for (int i = 0; i < columnSpecs.size(); i++) {
            if ("COMMENT".equalsIgnoreCase(columnSpecs.get(i))) {
                useColumnExpression.setCommentText(columnSpecs.get(i + 1));
            }
            if ("NULL".equalsIgnoreCase(columnSpecs.get(i)) && i != 0 && columnSpecs.get(i - 1).equalsIgnoreCase("NOT")) {
                useColumnExpression.setNullAble("NOT NULL");
            }
            if ("DEFAULT".equalsIgnoreCase(columnSpecs.get(i))) {
                useColumnExpression.setDefaultValue(columnSpecs.get(i + 1));
            }
        }
        return useColumnExpression;
    }


    private Optional getDropColumnExpression(
            final AlterOperation alterOperation,
            final String tableName,
            final String columnName
    ) {
        if (alterOperation != AlterOperation.DROP || columnName == null) {
            return Optional.empty();
        }

        AlterColumnExpression dropColumnExpression = new AlterColumnExpression();
        dropColumnExpression.setTableName(cleanText(tableName));
        dropColumnExpression.setColumnName(cleanText(columnName));
        dropColumnExpression.setOperation(EnhancedAlterOperation.DROP_COLUMN);
        return Optional.of(dropColumnExpression);
    }

    private String cleanText(String element) {
        return element.replace("`", "");
    }

    private String cleanMysqlAlterSql(String mysqlAlterSql) {
        return mysqlAlterSql.replace("COLLATE", "")
                .replace("collate", "");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy