解决FLINK CDC监控oracle报Failed to parse insert DML问题

  更多优秀文章,请扫码关注个人微信公众号或搜索“程序猿小杨”添加。

一、错误展现:

Caused by: io.debezium.connector.oracle.logminer.parser.DmlParserException: Failed to parse insert DML: 'insert into "HIS_DATA". 
	at io.debezium.connector.oracle.logminer.parser.LogMinerDmlParser.parseInsert(LogMinerDmlParser.java:109)
	at io.debezium.connector.oracle.logminer.parser.LogMinerDmlParser.parse(LogMinerDmlParser.java:73)
	at io.debezium.connector.oracle.logminer.processor.AbstractLogMinerEventProcessor.parseDmlStatement(AbstractLogMinerEventProcessor.java:1078)
	... 16 common frames omitted
Caused by: java.lang.ArrayIndexOutOfBoundsException: 74
	at io.debezium.connector.oracle.logminer.parser.LogMinerDmlParser.parseColumnListClause(LogMinerDmlParser.java:239)
	at io.debezium.connector.oracle.logminer.parser.LogMinerDmlParser.parseInsert(LogMinerDmlParser.java:100)
	... 18 common frames omitted

二、问题原因:

通过分析源代码,发现debezium-connector-oracle-1.9.7.Final中的方法存在问题,造成数组越界,一般方式场景在:oracle某张表表结构发生新增字段,然后flink cdc的任务加载的还是旧的表字段信息,当被监控表业务场景写入数据的时候,由于sql里面已经带了新增字段,但是table里面的字段还是旧的,造成解析错误,源代码如下:

 private int parseColumnListClause(String sql, int start, String[] columnNames) {
        int index = start;
        boolean inQuote = false;

        for(int var6 = 0; index < sql.length(); ++index) {
            char c = sql.charAt(index);
            if (c == '(' && !inQuote) {
                start = index + 1;
            } else {
                if (c == ')' && !inQuote) {
                    ++index;
                    break;
                }

                if (c == '"') {
                    if (inQuote) {
                        inQuote = false;
                        columnNames[var6++] = sql.substring(start + 1, index);
                        start = index + 2;
                    } else {
                        inQuote = true;
                    }
                }
            }
        }

        return index;
    }

三、解决方案:

解决思路:重写io.debezium.connector.oracle.logminer.parser下的LogMinerDmlParser类中的方法即可,方案有两种:

方案1:如果新增字段的内容不需要,比如:只需获取主键信息及对应的值,那就忽略掉新增的字段,新增字段内容不包含在解析后的数据中。那只需要调整一下代码即可:

在代码中新建一个包路径为:io.debezium.connector.oracle.logminer.parser,

复制源码路径下的LogMinerDmlParser类中内容:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package io.debezium.connector.oracle.logminer.parser;

import io.debezium.DebeziumException;
import io.debezium.connector.oracle.logminer.LogMinerHelper;
import io.debezium.relational.Column;
import io.debezium.relational.Table;

public class LogMinerDmlParser implements DmlParser {
    private static final String NULL_SENTINEL = "${DBZ_NULL}";
    private static final String NULL = "NULL";
    private static final String INSERT_INTO = "insert into ";
    private static final String UPDATE = "update ";
    private static final String DELETE_FROM = "delete from ";
    private static final String AND = "and ";
    private static final String OR = "or ";
    private static final String SET = " set ";
    private static final String WHERE = " where ";
    private static final String VALUES = " values ";
    private static final String IS_NULL = "IS NULL";
    private static final String UNSUPPORTED = "Unsupported";
    private static final String UNSUPPORTED_TYPE = "Unsupported Type";
    private static final int INSERT_INTO_LENGTH = "insert into ".length();
    private static final int UPDATE_LENGTH = "update ".length();
    private static final int DELETE_FROM_LENGTH = "delete from ".length();
    private static final int VALUES_LENGTH = " values ".length();
    private static final int SET_LENGTH = " set ".length();
    private static final int WHERE_LENGTH = " where ".length();

    public LogMinerDmlParser() {
    }

    public LogMinerDmlEntry parse(String sql, Table table) {
        if (table == null) {
            throw new DmlParserException("DML parser requires a non-null table");
        } else {
            if (sql != null && sql.length() > 0) {
                switch(sql.charAt(0)) {
                case 'd':
                    return this.parseDelete(sql, table);
                case 'i':
                    return this.parseInsert(sql, table);
                case 'u':
                    return this.parseUpdate(sql, table);
                }
            }

            throw new DmlParserException("Unknown supported SQL '" + sql + "'");
        }
    }

    private LogMinerDmlEntry parseInsert(String sql, Table table) {
        try {
            int index = INSERT_INTO_LENGTH;
            index = this.parseTableName(sql, index);
            String[] columnNames = new String[table.columns().size()];
            index = this.parseColumnListClause(sql, index, columnNames);
            Object[] newValues = new Object[table.columns().size()];
            this.parseColumnValuesClause(sql, index, columnNames, newValues, table);
            return LogMinerDmlEntryImpl.forInsert(newValues);
        } catch (Exception var6) {
            throw new DmlParserException("Failed to parse insert DML: '" + sql + "'", var6);
        }
    }

    private LogMinerDmlEntry parseUpdate(String sql, Table table) {
        try {
            int index = UPDATE_LENGTH;
            index = this.parseTableName(sql, index);
            Object[] newValues = new Object[table.columns().size()];
            index = this.parseSetClause(sql, index, newValues, table);
            Object[] oldValues = new Object[table.columns().size()];
            this.parseWhereClause(sql, index, oldValues, table);

            for(int i = 0; i < oldValues.length; ++i) {
                oldValues[i] = ParserUtils.getColumnUnavailableValue(oldValues[i], (Column)table.columns().get(i));
                if (newValues[i] == "${DBZ_NULL}") {
                    newValues[i] = null;
                } else if (newValues[i] == null) {
                    newValues[i] = oldValues[i];
                }
            }

            return LogMinerDmlEntryImpl.forUpdate(newValues, oldValues);
        } catch (Exception var7) {
            throw new DmlParserException("Failed to parse update DML: '" + sql + "'", var7);
        }
    }

    private LogMinerDmlEntry parseDelete(String sql, Table table) {
        try {
            int index = DELETE_FROM_LENGTH;
            index = this.parseTableName(sql, index);
            Object[] oldValues = new Object[table.columns().size()];
            this.parseWhereClause(sql, index, oldValues, table);
            ParserUtils.setColumnUnavailableValues(oldValues, table);
            return LogMinerDmlEntryImpl.forDelete(oldValues);
        } catch (Exception var5) {
            throw new DmlParserException("Failed to parse delete DML: '" + sql + "'", var5);
        }
    }

    private int parseTableName(String sql, int index) {
        for(boolean inQuote = false; index < sql.length(); ++index) {
            char c = sql.charAt(index);
            if (c == '"') {
                if (inQuote) {
                    inQuote = false;
                } else {
                    inQuote = true;
                }
            } else if ((c == ' ' || c == '(') && !inQuote) {
                break;
            }
        }

        return index;
    }

    private int parseColumnListClause(String sql, int start, String[] columnNames) {
        int index = start;
        boolean inQuote = false;

        for(int var6 = 0; index < sql.length(); ++index) {
            char c = sql.charAt(index);
            if (c == '(' && !inQuote) {
                start = index + 1;
            } else {
                if (c == ')' && !inQuote) {
                    ++index;
                    break;
                }

                if (c == '"') {
                    if (inQuote) {
                        inQuote = false;
                        columnNames[var6++] = sql.substring(start + 1, index);
                        start = index + 2;
                    } else {
                        inQuote = true;
                    }
                }
            }
        }

        return index;
    }

    private int parseColumnValuesClause(String sql, int start, String[] columnNames, Object[] values, Table table) {
        int nested = 0;
        boolean inQuote = false;
        boolean inValues = false;
        if (sql.indexOf(" values ", start) != start) {
            throw new DebeziumException("Failed to parse DML: " + sql);
        } else {
            int index = start + VALUES_LENGTH;
            int columnIndex = 0;

            for(StringBuilder collectedValue = null; index < sql.length(); ++index) {
                char c = sql.charAt(index);
                if (inQuote) {
                    if (c != '\'') {
                        collectedValue.append(c);
                    } else if (sql.charAt(index + 1) == '\'') {
                        collectedValue.append('\'');
                        ++index;
                        continue;
                    }
                }

                if (c == '(' && !inQuote && !inValues) {
                    inValues = true;
                    start = index + 1;
                } else if (c == '(' && !inQuote) {
                    ++nested;
                } else if (c == '\'') {
                    if (inQuote) {
                        inQuote = false;
                    } else {
                        inQuote = true;
                        collectedValue = new StringBuilder();
                    }
                } else if (!inQuote && (c == ',' || c == ')')) {
                    if (c == ')' && nested != 0) {
                        --nested;
                    } else if (c != ',' || nested == 0) {
                        if (sql.charAt(start) == '\'' && sql.charAt(index - 1) == '\'') {
                            int position = LogMinerHelper.getColumnIndexByName(columnNames[columnIndex], table);
                            values[position] = collectedValue.toString();
                            collectedValue = null;
                        } else {
                            String s = sql.substring(start, index);
                            if (!s.equals("Unsupported Type") && !s.equals("NULL")) {
                                int position = LogMinerHelper.getColumnIndexByName(columnNames[columnIndex], table);
                                values[position] = s;
                            }
                        }

                        ++columnIndex;
                        start = index + 1;
                    }
                }
            }

            return index;
        }
    }

    private int parseSetClause(String sql, int start, Object[] newValues, Table table) {
        boolean inDoubleQuote = false;
        boolean inSingleQuote = false;
        boolean inColumnName = true;
        boolean inColumnValue = false;
        boolean inSpecial = false;
        int nested = 0;
        int set = sql.indexOf(" set ", start);
        if (set == -1) {
            throw new DebeziumException("Failed to parse DML: " + sql);
        } else {
            if (set != start) {
                start = set;
            }

            start += SET_LENGTH;
            int index = start;
            String currentColumnName = null;

            for(StringBuilder collectedValue = null; index < sql.length(); ++index) {
                char c = sql.charAt(index);
                char lookAhead = index + 1 < sql.length() ? sql.charAt(index + 1) : 0;
                if (inSingleQuote) {
                    if (c != '\'') {
                        collectedValue.append(c);
                    } else if (lookAhead == '\'') {
                        collectedValue.append('\'');
                        ++index;
                        continue;
                    }
                }

                if (c == '"' && inColumnName) {
                    if (inDoubleQuote) {
                        inDoubleQuote = false;
                        currentColumnName = sql.substring(start + 1, index);
                        start = index + 1;
                        inColumnName = false;
                    } else {
                        inDoubleQuote = true;
                        start = index;
                    }
                } else if (c == '=' && !inColumnName && !inColumnValue) {
                    inColumnValue = true;
                    ++index;
                    start = index + 1;
                } else if (nested != 0 || c != ' ' || lookAhead != '|') {
                    int position;
                    if (nested == 0 && c == '|' && lookAhead == '|' && !inSingleQuote) {
                        for(position = index + 2; position < sql.length(); ++position) {
                            if (sql.charAt(position) != ' ') {
                                index = position - 1;
                                break;
                            }
                        }
                    } else if (c == '\'' && inColumnValue) {
                        if (inSingleQuote && lookAhead == '\'') {
                            ++index;
                        } else if (inSingleQuote) {
                            inSingleQuote = false;
                            if (nested == 0) {
                                position = LogMinerHelper.getColumnIndexByName(currentColumnName, table);
                                newValues[position] = collectedValue.toString();
                                collectedValue = null;
                                start = index + 1;
                                inColumnValue = false;
                                inColumnName = false;
                            }
                        } else {
                            if (!inSpecial) {
                                start = index;
                            }

                            inSingleQuote = true;
                            collectedValue = new StringBuilder();
                        }
                    } else if (c == ',' && !inColumnValue && !inColumnName) {
                        inColumnName = true;
                        ++index;
                        start = index;
                    } else if (inColumnValue && !inSingleQuote) {
                        if (!inSpecial) {
                            start = index;
                            inSpecial = true;
                        }

                        if (c == '(') {
                            ++nested;
                        } else if (c == ')' && nested > 0) {
                            --nested;
                        } else if ((c == ',' || c == ' ' || c == ';') && nested == 0) {
                            String value = sql.substring(start, index);
                            int position;
                            if (!value.equals("NULL") && !value.equals("Unsupported Type")) {
                                if (!value.equals("Unsupported")) {
                                    position = LogMinerHelper.getColumnIndexByName(currentColumnName, table);
                                    newValues[position] = value;
                                    start = index + 1;
                                    inColumnValue = false;
                                    inSpecial = false;
                                    inColumnName = true;
                                }
                            } else {
                                if (value.equals("NULL")) {
                                    position = LogMinerHelper.getColumnIndexByName(currentColumnName, table);
                                    newValues[position] = "${DBZ_NULL}";
                                }

                                start = index + 1;
                                inColumnValue = false;
                                inSpecial = false;
                                inColumnName = true;
                            }
                        }
                    } else if (!inDoubleQuote && !inSingleQuote && c == 'w' && lookAhead == 'h' && sql.indexOf(" where ", index - 1) == index - 1) {
                        --index;
                        break;
                    }
                }
            }

            return index;
        }
    }

    private int parseWhereClause(String sql, int start, Object[] values, Table table) {
        int nested = 0;
        boolean inColumnName = true;
        boolean inColumnValue = false;
        boolean inDoubleQuote = false;
        boolean inSingleQuote = false;
        boolean inSpecial = false;
        if (start >= sql.length()) {
            return start;
        } else {
            int where = sql.indexOf(" where ", start);
            if (where == -1) {
                throw new DebeziumException("Failed to parse DML: " + sql);
            } else {
                if (where != start) {
                    start = where;
                }

                start += WHERE_LENGTH;
                int index = start;
                String currentColumnName = null;

                for(StringBuilder collectedValue = null; index < sql.length(); ++index) {
                    char c = sql.charAt(index);
                    char lookAhead = index + 1 < sql.length() ? sql.charAt(index + 1) : 0;
                    if (inSingleQuote) {
                        if (c != '\'') {
                            collectedValue.append(c);
                        } else if (lookAhead == '\'') {
                            collectedValue.append('\'');
                            ++index;
                            continue;
                        }
                    }

                    if (c == '"' && inColumnName) {
                        if (inDoubleQuote) {
                            inDoubleQuote = false;
                            currentColumnName = sql.substring(start + 1, index);
                            start = index + 1;
                            inColumnName = false;
                        } else {
                            inDoubleQuote = true;
                            start = index;
                        }
                    } else if (c == '=' && !inColumnName && !inColumnValue) {
                        inColumnValue = true;
                        ++index;
                        start = index + 1;
                    } else if (c == 'I' && !inColumnName && !inColumnValue) {
                        if (sql.indexOf("IS NULL", index) == index) {
                            index += 6;
                            start = index;
                        }
                    } else {
                        int i;
                        if (c == '\'' && inColumnValue) {
                            if (inSingleQuote && lookAhead == '\'') {
                                ++index;
                            } else if (inSingleQuote) {
                                inSingleQuote = false;
                                if (nested == 0) {
                                    i = LogMinerHelper.getColumnIndexByName(currentColumnName, table);
                                    values[i] = collectedValue.toString();
                                    collectedValue = null;
                                    start = index + 1;
                                    inColumnValue = false;
                                    inColumnName = false;
                                }
                            } else {
                                if (!inSpecial) {
                                    start = index;
                                }

                                inSingleQuote = true;
                                collectedValue = new StringBuilder();
                            }
                        } else if (inColumnValue && !inSingleQuote) {
                            if (!inSpecial) {
                                start = index;
                                inSpecial = true;
                            }

                            if (c == '(') {
                                ++nested;
                            } else if (c == ')' && nested > 0) {
                                --nested;
                            } else if (nested != 0 || c != ' ' || lookAhead != '|') {
                                if (nested == 0 && c == '|' && lookAhead == '|') {
                                    for(i = index + 2; i < sql.length(); ++i) {
                                        if (sql.charAt(i) != ' ') {
                                            index = i - 1;
                                            break;
                                        }
                                    }
                                } else if ((c == ';' || c == ' ') && nested == 0) {
                                    String value = sql.substring(start, index);
                                    if (!value.equals("NULL") && !value.equals("Unsupported Type")) {
                                        if (!value.equals("Unsupported")) {
                                            int position = LogMinerHelper.getColumnIndexByName(currentColumnName, table);
                                            values[position] = value;
                                            start = index + 1;
                                            inColumnValue = false;
                                            inSpecial = false;
                                            inColumnName = true;
                                        }
                                    } else {
                                        start = index + 1;
                                        inColumnValue = false;
                                        inSpecial = false;
                                        inColumnName = true;
                                    }
                                }
                            }
                        } else if (!inColumnValue && !inColumnName) {
                            if (c == 'a' && lookAhead == 'n' && sql.indexOf("and ", index) == index) {
                                index += 3;
                                start = index;
                                inColumnName = true;
                            } else if (c == 'o' && lookAhead == 'r' && sql.indexOf("or ", index) == index) {
                                index += 2;
                                start = index;
                                inColumnName = true;
                            }
                        }
                    }
                }

                return index;
            }
        }
    }
}

调整其中的内容为:

if (inQuote) {
                        inQuote = false;
                        //调整方法即可
                        int columnNamesNum=columnIndex++;
//                        log.info("columnNamesNum的长度为:{}",columnNamesNum);
                        if(columnNamesNum < columnNames.length){
                            //log.info("columnNames[columnNamesNum]的值为:"+sql.substring(start + 1, index));
                            columnNames[columnNamesNum] = sql.substring(start + 1, index);
                            start = index + 2;
                        }
                    } else {
                        inQuote = true;
                    }

方案2:如果需要新增字段的值,那就利用sql中的字段去解析获取对应的值,不要使用table中的列,这块代码需要自己发挥。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值